Split Game 是2020ICPC 江西省大学生程序设计竞赛的题
剪纸游戏是acwing的一个博弈论题
我们对比两个很相似但是不一样的博弈论问题
题意:
Split Game :一张n * m的纸,两个人轮流操作,可以沿着一条直线切,当有人切出1 * 1的纸时即为输掉比赛,问最后谁赢
剪纸游戏:一张n * m的纸,两个人轮流操作,可以沿着一条直线切,当有人切出1 * 1的纸时即为赢得比赛,问最后谁赢
分析
题目很相似,区别在于先切出1 * 1 的是赢是输
那是不是我们按照第一个题Split game的思路来做第二个题,是不是就能a?
不完全是
第一个题是常规的有向图题
在第二个题中,并不是传统的有向图游戏,传统的有向图游戏定义是:当无路可走时游戏结束,而第二题是达到某种情况时,游戏直接结束
注意下,在第一个题中我们将1 * 1定义为必败状态,也就是当所有人都剪不了纸时游戏结束了
但是第二个题是当有人剪除1 * 1时,游戏立刻终止,无论双方还能否操作,游戏直接结束
也就是说在第一种情况中会尽可能不剪出1 * 1,除非没办法,而在第二种情况中会尽可能剪除1 * 1
在代码中除了剪切部分,其他都一样
split game的剪切是
for(int x=1;x<=n-1;x++){ if((x==1&&m==1)||(x==n-1&&m==1)) continue; ans = dfs(x,m)^dfs(n-x,m); vis[ans] = 1; } for(int y=1;y<=m-1;y++){ if((y==1&&n==1)||(y==m-1&&n==1)) continue; ans = dfs(n,y)^dfs(n,m-y); vis[ans] = 1; }
你会发现我们我们除了 1 * 1 的不能减,其他都OK
因为剪除1 * 1的边会输,所以我们除了不能让自己直接数(即剪除1 * 1 ),我们可以剪除1的边,来引诱对方输
剪纸游戏是:
for(int x=2;x<n-1;x++){ //if((x==1&&m==1)||(x==n-1&&m==1)) continue; ans = dfs(x,m)^dfs(n-x,m); vis[ans] = 1; } for(int y=2;y<m-1;y++){ //if((y==1&&n==1)||(y==m-1&&n==1)) continue; ans = dfs(n,y)^dfs(n,m-y); vis[ans] = 1; }
我们从2开始,尽可能避免出现边为1的情况,因为1 * 1 必胜,如果我们剪除一条边是1的情况,那轮到对方操作,对方可以直接剪除1 * 1 (我们贡献了一个1),为了保证我们自己获胜,我们肯定不会允许这种情况发生,所以会避免剪除1的边
所以第二个题
1 * 1 必胜我们可以将其上一个状态变为必败
或者x不从1开始,我们从2开始,直接跳过非法情况(也就是我们不按照第一条减,!=1,也不等于n-1)
有点啰嗦,大致如此
第一题代码:
#pragma GCC optimize("Ofast") #include<bits/stdc++.h> #define MAXN 205 using namespace std; typedef long long ll; int N,M; int sg[MAXN][MAXN]; int dfs(int n, int m){ if(n > m) swap(n,m); if(sg[n][m]!=-1) return sg[n][m]; if(n==1 && m==1) return sg[n][m] = 0; //cerr<<"dfs: "<<n<<" "<<m<<endl; int vis[MAXN]; memset(vis, 0, sizeof(vis)); int ans; for(int x=1;x<=n-1;x++){ if((x==1&&m==1)||(x==n-1&&m==1)) continue; ans = dfs(x,m)^dfs(n-x,m); vis[ans] = 1; } for(int y=1;y<=m-1;y++){ if((y==1&&n==1)||(y==m-1&&n==1)) continue; ans = dfs(n,y)^dfs(n,m-y); vis[ans] = 1; } for(int i=0;i<=300;i++){ if(!vis[i]) return sg[n][m] = i; } } int main(){ memset(sg, -1, sizeof(sg)); while(cin>>N>>M){ if(N > M) swap(N, M); //dfs(N,M); if(dfs(N,M)) puts("Alice"); else puts("Bob"); } return 0; }
第二题代码:
#pragma GCC optimize("Ofast") #include<bits/stdc++.h> #define MAXN 205 using namespace std; typedef long long ll; int N,M; int sg[MAXN][MAXN]; int dfs(int n, int m){ if(n > m) swap(n,m); if(sg[n][m]!=-1) return sg[n][m]; if(n==1 && m==1) return sg[n][m] = 0; //cerr<<"dfs: "<<n<<" "<<m<<endl; int vis[MAXN]; memset(vis, 0, sizeof(vis)); int ans; for(int x=2;x<n-1;x++){ //if((x==1&&m==1)||(x==n-1&&m==1)) continue; ans = dfs(x,m)^dfs(n-x,m); vis[ans] = 1; } for(int y=2;y<m-1;y++){ //if((y==1&&n==1)||(y==m-1&&n==1)) continue; ans = dfs(n,y)^dfs(n,m-y); vis[ans] = 1; } for(int i=0;i<=300;i++){ if(!vis[i]) return sg[n][m] = i; } } int main(){ memset(sg, -1, sizeof(sg)); while(cin>>N>>M){ if(N > M) swap(N, M); //dfs(N,M); if(dfs(N,M)) puts("WIN"); else puts("LOSE"); } return 0; }