SG-分石子游戏

分石子问题

时间限制: 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

计算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");
    }
}        












评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值