博弈随笔(未完待续)
都是一年前做过博弈巨水题 题解也写过了
这里在列出来 再看一下博弈
把黑白子之间的距离看成石子数,每次距离缩短,就相当于拿走了一部分石子,如果对手将一枚棋子往两侧移,那么你只需要再把这枚棋子往中间移相同的步数,就等同于这枚棋子没有移动,那么我们就可以认为石子只会减少不会增加,所以就变成了NIM问题。
这个想法很好
就是一个sg
注意后继情况 若一次操作把整个集合分成了多个部分 那就和这个题的代码一样 sg异或一下 如下 (记得有个珠子的题 也是这样)
for(int j = 0; j < n; j++) { if(i - a[j] < 0) break; vis[sg[i - a[j]]] = 1; for(int k = 1; k <= i - a[j] - 1; k++) vis[sg[k] ^ sg[i - a[j] - k]] = 1; }
诺 这就是那个珠子的题
只不过这个是一个串 而不是链
那n - w 后是不是就是链了
就是先手先拿一次
然后再处理
int n, m; cin>> n >> m; printf("Case #%d: ",++kase); if(n < m) { cout<< "abcdxyzk" <<endl; //后手 continue; } init(n-m, m); //n - m后就相当于HUD - 2999中排队的情况 if(sg[n-m]) cout<< "abcdxyzk" <<endl; else cout<< "aekdycoin" <<endl;
有向图博弈
图上的博弈和普通的解法一样
这里v是u的后继情况
而出度为0的点v没有后继结点 所以此时sg[v] = 0;
int mex(int u) { if(sg[u] != -1) return sg[u]; bool vis[maxn]; //这个标记数组要放在里面 和普通求sg一样 每个点都有自己的一个vis 因为普通sg是循环所以放在外面就可以 而这里是递归 所以要放在里面 mem(vis, 0); for(int i=head[u]; i!=-1; i=Node[i].next) { node e = Node[i]; sg[e.v] = mex(e.v); //去找后继状态 vis[sg[e.v]] = 1; } for(int i=0; ; i++) if(!vis[i]) return i; return 0; }
有n堆,每堆最多取m个,最少取1个
分而治之就是个Bash
可以直接每堆Bash然后异或 也可以sg
int main() { int T; cin >> T; while(T--) { cin >> n; int w, l; int ret = 0; for(int i = 0; i < n; i++) { cin >> w >> l; ret ^= (w % (l + 1)); } if(ret != 0) cout << "No" << endl; else cout << "Yes" << endl; } return 0; }
int sg[maxn]; int n, u, v; void mex() { bool vis[maxn]; mem(sg, 0); for(int i=1; i<=u; i++) { mem(vis, 0); for(int j=1; j<=v; j++) { if(i < j) break; vis[sg[i-j]] = 1; } for(int j=0; ; j++) if(!vis[j]) { sg[i] = j; break; } } } int main() { int T; cin>> T; while(T--) { int res = 0; cin>> n; for(int i=0; i<n; i++) { cin>> u >> v; mex(); res ^= sg[u]; } if(res) cout<< "No" <<endl; else cout<< "Yes" <<endl; } return 0; }
受1730的影响,这个是不是就可以看作就是nim博弈
异或就好了
总结SG的模板
int sg[maxn]; //如果有多堆而每堆拿上界||下界都一样 那么就可以只用一次SG,石子总数选最大值 //如果每堆拿的上界||下界不一样,那么就要每堆都进行一次SG void SG() { mem(sg, 0); bool vis[maxn]; for(int i=1; i<=100; i++) //石子总数 一般取最大值即可 { mem(vis, 0); //每次更新vis for(int j=1; j<=i; j++) //每轮可以减去的个数遍历 { vis[sg[i-j]] = 1; //后继状态 int tot = i-j; for(int k=1; k<tot; k++) //链式中断后继状态 vis[sg[k]^sg[tot-k]] = 1; } for(int j=0; ; j++) //找到不在集合内且最小的 if(!vis[j]) { sg[i] = j; break; } } }
自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。