hdu3595 every-sg

传送门:https://vjudge.net/problem/HDU-3595

最近都在做博弈论的题目,然后从汇总(https://vjudge.net/article/501)上看到every-sg的题,就来写写了。

其实一开始是看网上最顶上的博客才看懂的(https://blog.csdn.net/qiqijianglu/article/details/7959723),这篇博客所说的其实是:every-sg问题就是若干个游戏共同进行,以最后结束的游戏结果为最后结果,所以对于每一个游戏,必胜态所走的步数是后继必败态步数最大+1,必败态步数是后继中最小必胜态步数+1。这个的确是对的。

但是看到大神的代码,发现枚举后继状态是一个一个找过去的,也就是i+=a这句话。感觉不妥,遂思考。

因为这题就相当于辗转相除法,a/b >=2 是必胜的(因为如果(a%b,b)是必胜,那么也就是(a%b+b,b)是必败,那么可以转到必败态,而如果(a%b,b)必败,那也可以转到必败态)。

而a/b==1的情况,就只能看(a%b,b)的状态了。也就是说必胜态后继只有一个必败态,也只能转到这个必败态,必败态后继只有一个必胜态,也只能转到这个必胜态,所以每个状态玩到结束的步数是确定的。所以枚举之后的状态感觉有点没必要。。。。(但是对于every-sg来说,思路是对的)

那么我们要看什么呢?问题是最后结束的游戏,所以我们要看每一个游戏的步数(之前说过每个游戏的步数是确定的),然后记录最多步数那一个,奇数先手赢,偶数后手赢。

都在注释里了,看代码吧。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int st[1000+8][1000+8];//每个状态玩到最后的步数 
 4 int find(int a,int b){  //找每个状态玩到最后的步数 
 5     if(a<b) swap(a,b);
 6     if(st[a][b]>=0) return st[a][b];  //记忆化搜索 
 7     if(a/b==1) return st[a][b]=find(a%b,b)+1;//如果a/b==1,只能转到 (a%b,b)
 8     int tem=find(a%b,b);  //记录(a%b,b)的步数 
 9     if(tem&1) st[a][b]=tem+2;  //tem奇数,就是(a%b,b)必胜,所以转到(a%b+b,b)这个必败态 
10     else st[a][b]=tem+1;//转到必败态,因为后继只有这个必败态了 ,总不可能转去必胜态给对手吧。 
11     return st[b][a]=st[a][b];
12 }
13 int main(){
14     int n;
15     memset(st,-1,sizeof(st));
16     for(int i=0;i<=1000;++i) st[i][0]=st[0][i]=0;  //其中一堆没有了,步数就是0啦 
17     while((~scanf("%d",&n)) && n){
18         int ans=0;
19         for(int i=1;i<=n;++i){
20             int a,b;scanf("%d %d",&a,&b);
21             if(a==0 || b==0) continue;
22             ans=max(ans,find(a,b));//记录最多步数那个游戏 
23         }
24         if(ans&1) puts("MM");
25         else puts("GG");
26     }
27     return 0;
28 }
View Code

猜你喜欢

转载自www.cnblogs.com/xiaobuxie/p/11282672.html
今日推荐