Being a Good Boy in Spring Festival
Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 3972 Accepted Submission(s): 2345
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
思路:如果所有堆的扑克牌数异或值等于0,那么先手为奇异局势(先手必输 在选手不犯错情况下)。由此某一堆扑克牌的异或值,等于剩余全部堆的异或值,那么此局是奇异局势,因为必胜局势必存在可以转换成奇异局势,那么某一堆的值 大于剩余堆扑克牌(也就是说这堆扑克牌可以取走某些数使其值等于剩余堆的异或数),那么这为一种必胜选择,遍历所有堆得出结果。
/***************************************************
*
* acm: hdu-1850
*
* title: Being a Good Boy in Spring Festival
*
* time: 2014.5.4
*
***************************************************/
//此题考察博弈论,Nim博弈
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i;
int j;
int k;
int M; //M堆扑克牌
while (~scanf("%d", &M) && M != 0)
{
int N[101] = {0};
int sum = 0; //除当前堆,剩余堆得异或值
int num = 0; //必胜结果的选择数
for (i=1; i<=M; i++)
{
scanf("%d", &N[i]); //每一堆扑克牌的数量
}
for (i=1; i<=M; i++)
{
k = i;
sum = 0;
for (j=1; j < k; j++) //当前堆之前的所有堆
{
sum ^= N[j];
}
for (j = i + 1; j <= M; j++)//当前堆之后的所有堆
{
sum ^= N[j];
}
if (N[k] > sum) //如果当前堆值大于 剩余堆的异或值 则结果胜利
{
num++;
}
}
printf("%d\n", num);
}
return 0;
}
还有种思路,算是上面程序的优化把
/***************************************************
*
* acm: hdu-1850
*
* title: Being a Good Boy in Spring Festival
*
* time: 2014.5.4
*
***************************************************/
/*
理论:a+b=s,有 b=s+a (a表示某分堆,b表示其余分堆的异或值,s为总异或值)
即总异或和s 对任意一个数a取异或 可以的得到其他所有数的异或值b。
思路:if (b = s + a < a) 成立
a可以减到b使其异或为0 则有必胜策略
e.g
s = 5 + 7 + 9 = 0101 + 0111 + 1001 = 1011B = 11D
b = s + a = 11 + 5 = 1011 + 0101 = 1110B = 14D
b = 7 + 9 = 0111 + 1001 = 1110B = 14D
because : a < b
so: 从5取任何数都无法转为必败点
if (s != 0) 遍历剩余所以堆, 必存在能转为必败点的某堆
*/
#include<stdio.h>
#define MAXSIZE 100
int main()
{
int M;
int N[MAXSIZE];
while (~scanf("%d", &M) && M != 0)
{
int i;
int sum = 0;
int num = 0;
for (i = 0; i < M; i++)
{
scanf("%d", &N[i]);
sum ^= N[i];
}
for (i = 0; i < M; i++)
{
if (N[i] > (N[i]^sum))
{
num++;
}
}
printf("%d\n", num);
}
return 0;
}