Being a Good Boy in Spring Festival
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 5146 Accepted Submission(s): 3085
Problem Description
一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧
陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐
如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~
下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧
陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐
如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~
下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”
Input
输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1<M<=100),表示扑克牌的堆数,紧接着一行包含M个整数Ni(1<=Ni<=1000000,i=1…M),分别表示M堆扑克的数量。M为0则表示输入数据的结束。
Output
如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。
Sample Input
3 5 7 9 0
Sample Output
1
Author
lcy
Source
Recommend
这道题用了nim游戏的原型(尼姆博弈),只要掌握了尼姆博弈的规律就很好解了!(详见百度百科......)
规定有这样一种比赛,有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,规定每次至少取一个,多者不限,最后取光者得胜。
这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情形。这种奇异局势也可以称为必败局.(对战双方熟知尼姆博弈的情况下,当前局势为奇异局势,则必败).计算机算法里面有一种叫做按位模2加,也叫做异或的运算,凡是奇异局势(a,b,c),一定满足a^b^c=0(即a异或b异或c=0).对于奇异局势(0,n,n)也一样,结果也是0。注意到异或运算的交换律和结合律,及a^a=0,(a^b)(^)(a^b)=(a^a)(^)(b^b)=0^0=0。
所以程序的思路便是利用了这个技巧!
我们首先求出所有堆异或后的值,再用这个值去对每一个堆进行异或,令res = x1^sum(sum为所有堆的异或值)如果res < x1的话,当前玩家就从x1中取走(x1-res)个,取走x1-res之后这样必然导致对方的堆的异或值为0,也就是奇异局势(必败点),这就是一种方案,遍历每一个堆,进行上面的断判就可以得到总的方案数。
ps:这里想说明一下,为什么res会出现<x1的情况而不是大于x1情况.因为所有堆异或后的值只有两个可能,(1)sum为0.(2)sum不为0.因为要保证先手人赢,counting!(1)sum为0异或任何数都为那个数本身.,此时为奇异局势(必败!res=xi).(2)sum不为0,异或xi之后,此时相当于没有异或xi,即当前值是除了xi的所有异或.所以x1-res之后必然导致奇异局势.即构成方法!
例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)。
例4。我们来实际进行一盘比赛看看:
甲:(7,8,9)->(1,8,9)奇异局势
乙:(1,8,9)->(1,8,4)
甲:(1,8,4)->(1,5,4)奇异局势
乙:(1,5,4)->(1,4,4)
甲:(1,4,4)->(0,4,4)奇异局势
乙:(0,4,4)->(0,4,2)
甲:(0.4,2)->(0,2,2)奇异局势
乙:(0,2,2)->(0,2,1)
甲:(0,2,1)->(0,1,1)奇异局势
乙:(0,1,1)->(0,1,0)
甲:(0,1,0)->(0,0,0)奇异局势
甲胜。
例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)。
例4。我们来实际进行一盘比赛看看:
甲:(7,8,9)->(1,8,9)奇异局势
乙:(1,8,9)->(1,8,4)
甲:(1,8,4)->(1,5,4)奇异局势
乙:(1,5,4)->(1,4,4)
甲:(1,4,4)->(0,4,4)奇异局势
乙:(0,4,4)->(0,4,2)
甲:(0.4,2)->(0,2,2)奇异局势
乙:(0,2,2)->(0,2,1)
甲:(0,2,1)->(0,1,1)奇异局势
乙:(0,1,1)->(0,1,0)
甲:(0,1,0)->(0,0,0)奇异局势
甲胜。
import java.io.*;
import java.util.*;
public class Main
{
public static void main(String[] args)
{
// TODO Auto-generated method stub
Scanner input = new Scanner(System.in);
while (input.hasNext())
{
int m = input.nextInt();
if (m == 0)
break;
int a[] = new int[m];
int nim = 0;
for (int i = 0; i < m; i++)
{
a[i] = input.nextInt();
nim ^= a[i]; //算出所有堆得异或值
}
int sum = 0;
for (int i = 0; i < m; i++)
{
if ((nim ^ a[i]) < a[i]) //如果总异或值与单个堆异或的值小于单个堆则存在方法!
{
sum++;
}
}
System.out.println(sum);
}
}
}