The most orz man(单堆NIM)

来源:互联网 发布:皮影客软件介绍 编辑:程序博客网 时间:2024/05/16 23:54

原题链接
问题 D: The most orz man
时间限制: 1 Sec 内存限制: 128 MB
提交: 103 解决: 36
[提交][状态][讨论版]
这里写图片描述
题目的大意就是说有一堆石子,每个人每次只可以取2的幂次方这么多,问先手的输赢。
这是一个典型的NIM的题,而且只有一堆,考虑一下,当先手面前剩下了0的时候先手是必输的,1,2个的时候先手一定是必胜的,而剩下3个的时候就无论你取1,2的情况(即对手剩下2,1的情况)你都是输的,而当你面前剩下4个的时候你就是必胜的,但是当你面前又剩下5个的时候你可以取1,2,4(4,3,1),这个时候你发现只要你取2,对手就是必输的,因为你把必输的情况推给了后手,后手输当然先手就赢了。
所以这个时候就可以模拟每次你可以取的所有情况然后判断对手是不是有一种情况是必输的,只要找到一种这样的情况那你就是必赢的。

#include <iostream>#include <cstdio>#include <cstring>using namespace std;int a[1000];int p[1000];int main(){        memset(a,0,sizeof(a));        p[0]=1;        for(int i=1;i<30;i++)                p[i]=p[i-1]*2;        a[0]=0;a[1]=1;a[2]=1;        for(int i=3;i<200;i++){                a[i]==0;                for(int j=0;p[j]<=i;j++){                        int t=i-p[j];                        if(a[t]==0)                                a[i]=1;                }        }        for(int i=0;i<200;i++) cout << a[i] << endl;}

打表之后的结果如下
这里写图片描述
于是就发现了一个简单的规律那就是当前所剩子数为模3为0的时候是必败的,而当前所剩子数模3不为0的时候,总可以有一种操作使得后手所面对的石子数模3为0,所以这中情况下是必胜的。
所以最后的答案就是看模三之后的结果就可以了

int main(){        int n;        while(scanf("%d",&n)==1){                if(n%3==0) printf("No\n");                else printf("Yes\n");        }        return 0;}

但是这只是单堆的情况,当推广到多堆的情况的时候我们就需要引入一个新的函数叫做sg函数,然后还要知道一个定理就是:假设a[i]代表第i堆有a[i]个石头,那么如果对于当前的状态,如果
a[1]^a[2]^a[3]^……..^a[n-1]^a[n]==0的话,那么谁在这个状态那么谁就是必败的。而且如果在这个状态异或的结果不为0的话那么他一定是必胜的。
所以关键就是算出这个sg[i] 。
这里有两种方法,第一种是打表找规律,只要找到sg函数的规律就可以不用每次都进行运算了
如果没有规律就需要进行运算,这里可以提前进行预处理,也可以每需要算一个就算一个。由于不同的题询问次数与数据范围不一样,所以到底是先预处理好还是每次需要算的时候再去算好就需要看题了。

0 0
原创粉丝点击