组合博弈
博弈,一般就是,sg啊,dfs极大极小搜索啊,dp啊,找规律啊....
HDU 1847 Good Luck in CET-4 Everybody!(SG水题)
预处理一下,不然会RE。
这个题,非常棒...看了别人的思路,n个1个格子的sg为n%2,我们利用sg函数处理出2*i的值,然后把给出的矩形分段。
#include <cstdio> #include <cstring> #include <map> #include <algorithm> #include <iostream> using namespace std; int dp[5001]; int p[3][5001]; int sg(int x) { int i,l,r; int o[5001]; if(dp[x] >= 0) return dp[x]; memset(o,0,sizeof(o)); for(i = 1;i <= x;i ++) { l = i-1;//染1*1的方格 r = x-i; o[sg(l)^sg(r)^1] = 1; if(i+1 <= x)//枚举在那里染2*2的方格 { l = i-1; r = x-i-1; o[sg(l)^sg(r)] = 1; } } for(i = 0;;i ++) { if(!o[i]) return dp[x] = i; } return 0; } int main() { int n,m,x,y,i,t,cas = 1,ans,flag; memset(dp,-1,sizeof(dp)); dp[0] = 0; dp[1] = 0; for(i = 2;i <= 5000;i ++) { sg(i); } scanf("%d",&t); while(t--) { memset(p,0,sizeof(p)); scanf("%d%d",&n,&m); for(i = 0;i < m;i ++) { scanf("%d%d",&x,&y); p[x][y] = 1; } ans = (2*n-m)&1; flag = 0; for(i = 1;i <= n;i ++) { if(!p[1][i]&&!p[2][i]) flag ++; else { ans ^= dp[flag]; flag = 0; } } ans ^= dp[flag]; printf("Case %d: ",cas++); if(ans) printf("Alice\n"); else printf("Bob\n"); } return 0; }
极大极小dfs
#include <cstdio> #include <cstring> #include <map> #include <algorithm> #include <iostream> using namespace std; char str[50][50]; int p[50][50]; int n,m; int dfs() { int i,j,flag; flag = 0; for(i = 0;i < n-1;i ++) { for(j = 0;j < m-1;j ++) { if(p[i][j] == 0&&p[i+1][j] == 0&&p[i][j+1] == 0&&p[i+1][j+1] == 0) { p[i][j] = p[i+1][j] = p[i][j+1] = p[i+1][j+1] = 1; if(dfs() == 0) flag = 1; p[i][j] = p[i+1][j] = p[i][j+1] = p[i+1][j+1] = 0; } } } if(flag) return 1; else return 0; } int main() { int i,j; while(scanf("%d%d",&n,&m)!=EOF) { for(i = 0; i < n; i ++) scanf("%s",str[i]); for(i = 0; i < n; i ++) { for(j = 0; j < m; j ++) { if(str[i][j] == '0') p[i][j] = 0; else p[i][j] = 1; } } if(dfs()) printf("Yes\n"); else printf("No\n"); } return 0; }
这题先不考虑有环的情况,长度为n的sg会比较好求,暴力出sg会发现,除了k = 1之外,其他情况的k,dp[i] >= 1,无论第一步怎么选,肯定是要变化为一段的情况。
搜了一下题解,发现大家直接搞出了 策略,后手可以搞出对称的策略,用sg对不对啊,应该也没问题吧...
#include <cstdio> #include <cstring> #include <map> #include <algorithm> #include <iostream> using namespace std; int dp[1001]; int n; int sg(int x) { int i,j; if(dp[x] >= 0) return dp[x]; int o[1001]; memset(o,0,sizeof(o)); for(i = 1;i <= x;i ++) { for(j = 1;j <= n;j ++) { if(x-j-(i-1) < 0) continue; o[sg(i-1)^sg(x-j-(i-1))] = 1; } } for(i = 0;;i ++) if(!o[i]) return dp[x] = i; } int main() { int t,k,cas = 1; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&k); printf("Case %d: ",cas++); if(k == 1) { if(n%2) printf("first\n"); else printf("second\n"); } else { if(n <= k) printf("first\n"); else printf("second\n"); } } return 0; }
完全木有想法,看的题解,说这是阶梯博弈...
这个题,1 3 4上的石子没法移动了,%3之后的变化0->0 1->2 2->1,其实原理也不是很懂, 打表或许是个好办法。%6取余之后,走到最后的奇偶性是确定的。然后对奇数的情况,异或一下,不懂啊...
#include <cstdio> using namespace std; int main() { int t,cas = 1,i,x,ans,n; scanf("%d",&t); while(t--) { scanf("%d",&n); ans = 0; for(i = 1;i <= n;i ++) { scanf("%d",&x); if(i%6 == 0||i%6 == 2||i%6 == 5) ans ^= x; } printf("Case %d: ",cas++); if(ans) printf("Alice\n"); else printf("Bob\n"); } return 0; }
还真是一个simple game,每一堆石子的sg(x)= x%(y+1),打个表看一下就好。
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cstdlib> #include <algorithm> using namespace std; #define LL long long #define MOD 1000000009 int main() { int i,t,n,x,y,ans; scanf("%d",&t); while(t--) { scanf("%d",&n); ans = 0; for(i = 0;i < n;i ++) { scanf("%d%d",&x,&y); ans ^= (x%(y+1)); } if(ans) printf("No\n"); else printf("Yes\n"); } return 0; }
和之前一个题 很类似,把游戏划分成 在x,y上有炸弹,其他地方都没炸弹的情况。求出sg值,然后把有炸弹的位置,异或一下。
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cstdlib> #include <algorithm> using namespace std; #define LL long long #define MOD 1000000009 int dp[51][51]; int sg(int x,int y) { int i,j; int o[1001]; if(dp[x][y] >= 0) return dp[x][y]; memset(o,0,sizeof(o)); if(x > 0&&y > 0) { for(i = 0; i < x; i ++) { for(j = 0; j < y; j ++) o[sg(i,y)^sg(x,j)] = 1; } } else if(x == 0) { for(j = 0; j < y; j ++) o[sg(x,j)] = 1; } else { for(i = 0; i < x; i ++) { o[sg(i,y)] = 1; } } for(i = 0;;i ++) { if(!o[i]) return dp[x][y] = i; } } int main() { int i,j,n,m,ans; char str[51][51]; memset(dp,-1,sizeof(dp)); dp[0][0] = 0; sg(0,1); while(scanf("%d%d",&n,&m)!=EOF) { if(!n&&!m) break; ans = 0; for(i = 0; i < n; i ++) { scanf("%s",str[i]); } for(i = 0; i < n; i ++) { for(j = 0; j < m; j ++) { if(str[i][j] == '#') ans ^= sg(i,j); } } if(ans) printf("John\n"); else printf("Jack\n"); } return 0; }
以下复制的。
这题与以往的博弈题的胜负条件不同,谁走完最后一步谁输,但它也是一类NIM游戏,记为anti-nim游戏
首先给出结论: 先手胜当且仅当
(1)所有堆石子数都为1且游戏的SG值为0 ,(2)存在某堆石子数大于1且游戏的SG值不为0
证明:
(1)若所有堆石子数都为1且SG值为0,则共有偶数堆石子,故先手胜。
(2)
i)只有一堆石子数大于1时,我们总可以对该堆石子操作,使操作后石子堆数为奇数且所有堆得石子数均为1
ii)有超过一堆石子数大于1时,先手将SG值变为0即可,且总还存在某堆石子数大于1
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cstdlib> #include <algorithm> using namespace std; #define LL long long int main() { int t,i,j,n,x,flag,ans; scanf("%d",&t); while(t--) { scanf("%d",&n); flag = 1; ans = 0; for(i = 0;i < n;i ++) { scanf("%d",&x); ans ^= x; if(x != 1) flag = 0; } if(flag) { if(n%2 == 0) printf("John\n"); else printf("Brother\n"); } else { if(ans) printf("John\n"); else printf("Brother\n"); } } return 0; }
这题是 anti-nim+树的删边游戏,叶子节点sg为0,其他的节点,为儿子节点sg值+1异或起来。注意这题,一棵树的sg值 可能会为0啊!
#include <iostream> #include <cstring> #include <cstdio> #include <queue> #include <cstdlib> #include <algorithm> using namespace std; #define LL long long struct node { int v,next; }edge[301]; int first[301]; int flag[301]; int sg[301]; int t; void add(int u,int v) { edge[t].v = v; edge[t].next = first[u]; first[u] = t ++; } void dfs(int x) { int i,v,ans = 0; flag[x] = 1; for(i = first[x];i != -1;i = edge[i].next) { v = edge[i].v; if(flag[v]) continue; dfs(v); ans ^= (sg[v] + 1); } sg[x] = ans; } int main() { int i,j,n,m,u,v,ans,z; while(scanf("%d",&n)!=EOF) { ans = 0; z = 0; for(i = 1;i <= n;i ++) { memset(first,-1,sizeof(first)); memset(flag,0,sizeof(flag)); t = 0; scanf("%d",&m); for(j = 1;j < m;j ++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1); ans ^= sg[1]; if(sg[1] > 1) z = 1; } if(z == 0) { if(ans) printf("QQ\n"); else printf("PP\n"); } else { if(ans) printf("PP\n"); else printf("QQ\n"); } } return 0; }