处理何种问题:有n堆物品,每堆物品数为 arr[i] ,双方轮流从中取物品,每次只能从一堆物品中取任意件物品,最少取一件,取到最后一件物品的人获胜,求谁最后获胜。
性能:因为是有公式,所以时间复杂度为O(n)。
原理:结论:把每堆物品数全部都异或起来,如果得到的值为0,那么先手必败,否则先手必胜。
在这里补充一个知识点,异或:相同为0,不同为1,同一些数异或的顺序不会对最终结果有何影响。
以三堆物品为例:
我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。
重点:任何奇异局势(a,b,c)都有a^b^c =0。
如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b< c,我们只要将 c 变为 a^b,即可,因为有如下的运算结果: a^b^(a^b)=(a^a)^(b^b)=0^0=0。要将c 变为a^b,只要从 c中减去 c-(a^b)即可。
例1。(14,21,39),14^21=27,39-27=12,所以从39中拿走12个物体即可达到奇异局势(14,21,27)。
例2。(55,81,121),55^81=102,121-102=19,所以从121中拿走19个物品就形成了奇异局势(55,81,102)。
例3。(29,45,58),29^45=48,58-48=10,从58中拿走10个,变为(29,45,48)。
备注:记住结论:异或结果为0,先手必败,否则必胜。
输入样例解释:
5//n堆
12 58 6 22 54//每堆石子个数
输出样例解释:
First Win //先手胜
#include<iostream> #include<cstdio> #include<algorithm> using namespace std; int main() { int n,temp,ans; while(~scanf("%d",&n)) { ans=0;// 任意一个数异或 0 ,都是该数 for(int i=0;i<n;++i) { scanf("%d",&temp); ans=(temp^ans);//异或运算符优先度较低,注意加括号 } if(ans) printf("First Win "); else printf("First Lose "); } return 0; }