分石子问题
时间限制:
2000 ms | 内存限制:
65535 KB
难度:
6
-
描述
-
闲来无事,zyc发明了一种游戏,叫分石子游戏,初始有D堆石子,每堆石子的数量已知。
两个人轮流分石子,可以选取这D堆石子中的任意一堆,然后把选中的这堆石子分成M堆(每堆石子数量都必须大于0),现在石子的堆数将变成D+M-1堆,对方就可以在这D+M-1堆石子中任意选一堆分成M堆,依次分下去,直到某人无法执行这种操作时则无法执行操作的人输掉了这场游戏。
如果玩游戏的双方都非常聪明,现在给你一个初始状态,请你判断先分石子的人是会取胜还是会失败。
-
输入
-
第一行是一个整数T,表示测试数据的组数(T<=10)
每组测试数据的第一行是两个整数D,M(0<D<100,2<=M<=7)
随后的一行有D个正整数Ni(Ni<100),表示每堆石子的初始数量。
输出
-
如果先分石子的人获胜则输出Win
否则输出Lose
样例输入
-
2 1 3 5 2 3 5 6
样例输出
-
Win Lose
-
第一行是一个整数T,表示测试数据的组数(T<=10)
计算sg函数,找规律:
#include <iostream>
#include <string.h>
using namespace std;
#define maxn 100
int a[maxn];
int vis[maxn];
int sg[maxn];
int ed[maxn];
int mm;
int fun(int n,int m,int minn,int rt){//n为剩下的石子数,m为剩下待分堆数,minn为先拿出的个数(不一定是石子数,可能后面会增长),rt为以分的堆数
ed[rt]=minn;//第rt次分了minn这么一堆
if(m==1){
ed[rt]=n;//最后一次只能是剩下的n个为一堆
int temp=sg[ed[0]];
for(int i=1;i<=rt;i++){
temp^=sg[ed[i]];//所有的堆分完了,利用sg值 计算NIM值,当作此中分发的sg值
}
vis[temp]=1;
}else if(m*minn <=n){//还可以分
fun(n-minn, m-1, minn, rt+1);//就以minn为一堆,分出去
fun(n, m, minn+1, rt);//再拿出一部分给minn,还不确定当前堆的石子数目
}
}
int main(){
int n;
cin>>n>>mm;
int ma=0;
for(int i=0;i<n;i++){
cin>>a[i];
ma=max(ma,a[i]);
}
for(int i=0;i<mm;i++)sg[i]=0;sg[mm]=1;
for(int i=mm+1;i<=ma;i++){
memset(vis,0,sizeof(vis));
fun(i,mm,1,0);
for(int j=0;;j++){
if(!vis[j]){
sg[i]=j;
break;
}
}
}
int sum=0;
for(int i=0;i<n;i++)
sum^=sg[a[i]];
if(sum)cout<<"win"<<endl;
else cout<<"lose"<<endl;
return 0;}
化简
代码:
#include <cstdio>
using namespace std;
int main()
{
int T, d, m;
for (scanf("%d", &T); T--; ) {
int a, sum = 0;
scanf("%d%d", &d, &m);
for (int i = 0; i < d; i++) {
scanf("%d", &a);
sum ^= m == 2 ? a > 1 && a%2 == 0 : a >= m ? (a-1)/(m-1) : 0;
}
puts(sum ? "Win" : "Lose");
}
}