取石子(三)
-
描述
-
小王喜欢与同事玩一些小游戏,今天他们选择了玩取石子。
游戏规则如下:共有N堆石子,已知每堆中石子的数量,两个人轮流取子,每次只能选择N堆石子中的一堆,取一定数量的石子(最少取一个),取过子之后,还可以将该堆石子中剩下的任意多个石子中随意选取几个放到其它的任意一堆或几堆上。等哪个人无法取子时就表示此人输掉了游戏。注意,一堆石子没有子之后,就不能再往此处放石子了。
假设每次都是小王先取石子,并且游戏双方都绝对聪明,现在给你石子的堆数、每堆石子的数量,请判断出小王能否获胜。
例如:如果最开始有4堆石子,石子个数分别为3 1 4 2,而小王想决定要先拿走第三堆石子中的两个石子(石子堆状态变为3 1 2 2),然后他可以使石子堆达到的状态有以下几种:
3 1 2 2(不再移动石子)
4 1 1 2(移动到第一堆一个)
3 2 1 2(移动到第二堆一个)
3 1 1 3(移动到第四堆一个)
5 1 0 2(全部移动到第一堆)
3 3 0 2(全部移动到第二堆)
3 1 0 4(全部移动到最后)
-
输入
-
可能有多组测试数据(测试数据组数不超过1000)
每组测试数据的第一行是一个整数,表示N(1<=N<=10)
第二行是N个整数分别表示该堆石子中石子的数量。(每堆石子数目不超过100)
当输入的N为0时,表示输入结束
输出
- 对于每组测试数据,输出Win表示小王可以获胜,输出Lose表示小王必然会败。 样例输入
-
3 2 1 3 2 1 1 0
样例输出
-
Win Lose
来源
- N先手胜局 P后手胜局
- 剩一堆石头这N局面;
- 剩两堆石头 1、(a,b) a<b;则可以换成(a,a)形式,跟随后手拿法即可胜利,即N局面;
- 2、(a,a) 与上相反,P局面;
- 剩三堆石头(a,b,c) 先手总能转化生(m,m,0)所以N局面;
- 剩四堆石头 1、(a,a,b,b) 则类似两堆局面,P局面;
- 2、(a,b,c,d) 总能转化成(n,n,m,m)形式,所以N局面;
- 推开总的来说:如果某一个数的石子堆数为奇数,则总能转化成(n,n,m,m,……)的形式,所以N局面;
- 如果局势总是(a,a,b,b,……)形式,与上相反,则P局面。
思路:一堆时,N态。两堆时,当两堆数量相同,P态,不同为N态。三堆时,先手可以变成两堆一样的,必胜N态。
此时可以总结规律:堆数为偶数可能且石子数都是两两相同的,为P态。分析四堆时,当四堆中两两数量一样的情况是P态,有一些数量不一样的情况:x < y < z < k , 可以通过拿k并分配剩下的石子,让四堆两两相同,即转换为P态。当五堆时,先手一定可以变成四堆并让四堆中的石子数两两相同。
至此,找到规律了:当n为奇数,必胜,当n为偶数且石子数两两对应为P态,否则为N态。
AC代码
#include <iostream> #include<cstdio> #include<cmath> #include<cstring> using namespace std; int f[102]; int main() { int n; while(~scanf("%d",&n)&&n) { memset(f,0,sizeof(f)); int a; for(int i=0;i<n;i++) { scanf("%d",&a); f[a]++; } if(n&1) printf("Win\n"); else { int i; for( i=1;i<=100;i++) { if(f[i]&1) break; } if(i<=100)printf("Win\n"); else printf("Lose\n"); } } return 0; }
-
可能有多组测试数据(测试数据组数不超过1000)