【POJ 2311】Cutting Game
阿新 • • 發佈:2020-12-22
題目
題目連結:http://poj.org/problem?id=2311
一張 \(n\times m\) 的紙,兩人輪流行動,每次行動需要在所有紙中選擇一張的一行或者一列剪斷,變為兩張新的紙。先剪出 \(1\times 1\) 的紙的人獲勝。求先手是否必勝。
思路
很明顯的公平組合遊戲,對於一個 \(n\times m\) 的紙,它所有候機狀態為
\[\left \{ (i\times m,(n-i)\times m) | 1\leq i<n\right \}\cup \left \{ (n\times i,n\times (m-i)) | 1\leq i<m\right \} \]由於 \(2\times 2,2\times 3,3\times 2,3\times 3\) 的紙時先手必敗,所以這些紙的 \(sg\) 值全部為 \(0\)。然後遞推取 \(\mathrm{mex}\) 即可。
時間複雜度 \(O(nm(n+m)+Q)\)。
程式碼
#include <cstdio> #include <cstring> using namespace std; const int N=210; int n,m,sg[N][N]; bool vis[N]; void dfs(int n,int m) { if (sg[n][m]!=-1) return; for (int i=2;i<n-1;i++) dfs(i,m); for (int i=2;i<m-1;i++) dfs(n,i); memset(vis,0,sizeof(vis)); for (int i=2;i<n-1;i++) vis[sg[i][m]^sg[n-i][m]]=1; for (int i=2;i<m-1;i++) vis[sg[n][i]^sg[n][m-i]]=1; sg[n][m]=0; for (int i=0;vis[i];i++) sg[n][m]=i+1; } int main() { memset(sg,-1,sizeof(sg)); sg[2][2]=sg[2][3]=sg[3][2]=sg[3][3]=0; for (int i=2;i<=200;i++) for (int j=2;j<=200;j++) dfs(i,j); while (scanf("%d%d",&n,&m)!=EOF) if (sg[n][m]) printf("WIN\n"); else printf("LOSE\n"); return 0; }