第十二届蓝桥杯Java省赛A组试题:异或数列
【題目描述】
初始時,Alice和Bob分別有一個整數a和b,有一個給定的長度為n的數列。a和b的初始值均為0。Alice和Bob輪流操作,Alice先手,每步可以從兩個選項中選一種:
選項1:從數列中選一個X;給Alice的數異或上。
選項2:從數列中選一個X;給Bob的數異或上。
每個數Xi只能用一次,當每個數都被用過一遍后,游戲結束。擁有數大的一方獲勝,雙方數字相同即平局。
雙方都足夠聰明,都采用最優策略,問誰能獲勝?
【輸入格式】
每個評測用例包含多組詢問。詢問之間彼此獨立。
輸入的第一行包含一個整數T,表示詢問數。
接下來T行每行包含一組詢問。其中第i行的第一個整數n;表示數列長度,隨后n個整數X1, X2,… Xm表示數列中的每個數。
【輸出格式】
輸出T行,依次對應每組詢問的答案。
每行包含一個整數1、0或-1分別表示Alice勝、平局或敗。
【樣例輸入】
4
1 1
1 0
2 2 1
7 992438 1006399 781139 985280 4729 872779 563580
【樣例輸出】
1
0
1
1
【評測用例規模與約定】
對于所有評測用例,1 ≤ T ≤ 200000,1 ≤ ∑Ti=1 ni ≤ 200000,0 ≤ Xi < 220。
【解題思路】
借鑒:該博客
首先明確,為什么給定一個數列,游戲的結局是確定的呢?
A贏的意思是:A能采取某種策略,不管B作何應對,A保證自己一定能贏;
B贏的意思是:B能采取某種策略,不管A作何應對,B保證自己一定能贏;
對于異或:0^X ——保持X ; 1^X——翻轉X;
偶數次翻轉將回到原來的狀態,即與偶數個1異或將保持不變。
最后兩個結果數A^B = a^b^sum ,其中sum為給定數列所有數的異或和,sum = X1^X2^...Xni,所以如果是平局(即A = B),則A^B=0(即 sum = 0)。
現在考慮sum != 0時,因為是按位異或,并且最后比較A和B兩值的大小,所以要從二進制的最高位開始比較,如果最高位相等,比較次高位,以此往下比較,直到可以判斷出結果。
用一個int型數組res[i]表示二進制的第i位上1的個數,如果某一位的res[i]為奇數,即該位上有奇數個1與a和b異或,比如有5個1,因為0不會改變狀態,其(a,b)的狀態轉移過程一定是:(0,0)->翻轉->平局->翻轉->平局->翻轉,也就是說在平局不管是(0,0)或(1,1)的基礎上,Alice 和 Bob誰擁有最后一次翻轉權(也就是最后一個1),誰就贏得游戲!
那怎么讓最后一個1輪到自己身上?就要用到0了,因為0不會改變數的大小,但相當于讓自己“輪空一次”。還是考慮上面5個1的情況:如果在最后一次翻轉之前(不管在什么位置)插入偶數個0,則最后一次1的翻轉權來到了先手 Alice,則先手勝出;如果在最后一次翻轉之前(不管在什么位置)插入奇數個0,則最后一次1的翻轉權來到了后手 Bob,則后手勝出。
因為Alice和Bob都“足夠聰明”,面對奇數個1,Bob一定要去“ 搶0 ”,自己才有一線生機,而Alice也要以“ 搶0 ”來作應對,讓最后一次翻轉權回到自己手里,因為0的個數有限,最后決定勝負的就是0個數的奇偶性。
因此,如果某一位的res[i]為偶數,則游戲結果的這一位一定相等,考慮下一位 ;如果第i位上1的個數為 1,則一定是先手贏(輸出:“1”);如果第i位上1的個數為大于1的奇數,0的個數為偶數,則先手贏(輸出:“1”);如果第i位上1的個數為大于1的奇數,0的個數為奇數,則后手贏(輸出:“-1”)。
Java代碼:
import java.util.ArrayList; import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();//該ArrayList用來暫存每次詢問結果,待最后一次詢問結束再統一輸出ArrayList<Integer> res = new ArrayList<>();//n次詢問for (int i = 0; i < n; i++) {int[] weis = new int[24]; //該數組用于存儲二進制形式下每一位1的數量int m = scanner.nextInt();//長度為m的數列轉為二進制并處理for (int j = 0; j < m; j++) {long l = scanner.nextLong();String string = Long.toBinaryString(l); //轉為二進制String reStr = new StringBuilder(string).reverse().toString(); //反轉,即高低位反轉,便于存儲與后面處理//判斷二進制形式下每一位是否為1for (int k = 0; k < reStr.length(); k++) {if (reStr.charAt(k) == '1') weis[k]++;}}//對每次詢問后每一位中1的個數判斷從而得到結果,并將結果先暫時存儲在ArrayList-res中for (int j = weis.length - 1; j >= 0; j--) {if (weis[j] % 2 == 0 && j == 0) {res.add(0);break;}else if (weis[j] % 2 == 0){continue;}if (weis[j] == 1){res.add(1);break;}else if (weis[j] % 2 == 1 && (m - weis[j]) % 2 == 0){res.add(1);break;}else if (weis[j] % 2 == 1 && (m - weis[j]) % 2 == 1){res.add(-1);break;}}}//輸出結果for (int x : res){System.out.println(x);}} }總結
以上是生活随笔為你收集整理的第十二届蓝桥杯Java省赛A组试题:异或数列的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux计数命令(linux计数)
- 下一篇: 第十二届蓝桥杯省赛:试题 A: 卡片(J