博弈论小结
博弈
P2575 高手过招
点击查看代码
// 转化为 n 个有向图游戏
#include <stdio.h>
#include <string.h>
#include <unordered_set>
int n, m, T, sg[1 << 20], st[1 << 20], res, state;
int SG(int x) {
if(st[x]) return sg[x];
st[x] = true;
std::unordered_set<int> set;
for(int i = 0; i < 20; i ++)
if(x >> i & 1)
for(int j = i; j < 20; j ++)
if(!(x >> j & 1)) {
set.insert(SG((x ^ (1 << i)) | (1 << j)));
break;
}
for(int i = 0; ; i ++)
if(!set.count(i)) return sg[x] = i;
}
int main() {
scanf("%d", &T);
while(T --) {
scanf("%d", &n), res = 0;
while(n --) {
scanf("%d", &m), state = 0;
for(int i = 0, x; i < m; i ++) scanf("%d", &x), state |= 1 << (x - 1);
res ^= SG(state);
}
puts(res ? "YES" : "NO");
}
return 0;
}
[SDOI2009] P2148 E&D
// 结论: 一组为 (x,y) 时 SG 函数为 (x-1)|(y-1) 最低的 0 所在的位置
// 每一对都可以看成一个有向图游戏: (a,b) -> (x,y) 其中 x+y = a或b
// 计算出 SG 函数, 发现结论
点击查看代码
#include <stdio.h>
#include <string.h>
#include <algorithm>
int main() {
int T, n, a, b, res, x, cnt;
scanf("%d", &T);
while(T --) {
scanf("%d", &n), res = 0, n >>= 1;
while(n --) {
scanf("%d%d", &a, &b), x = (a - 1) | (b - 1), cnt = 0;
while(x & 1) x >>= 1, cnt ++;
res ^= cnt;
}
puts(res ? "YES" : "NO");
}
return 0;
}
[USACO09NOV] P2964 A Coin Game S
f[i][j]表示前i个硬币最后一次B选择了j个硬币的A的最大收益
f[i][j] = max(f[i][j - 1], 选2*j-1,选j)
点击查看代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <queue>
using namespace std;
const int N = 2e3 + 5;
int n, w[N], sumw[N];
int f[N][N];
int main() {
scanf("%d", &n);
for(int i = n; i; i --) scanf("%d", w + i);
for(int i = 1; i <= n; i ++) sumw[i] = sumw[i - 1] + w[i];
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
f[i][j] = f[i][j - 1];
int k = 2 * j - 1;
if(k <= i) f[i][j] = max(f[i][j], sumw[i] - f[i - k][k]);
k = 2 * j;
if(k <= i) f[i][j] = max(f[i][j], sumw[i] - f[i - k][k]);
}
}
printf("%d\n", f[n][1]);
return 0;
}
[CQOI2013] P4576 棋盘游戏
对抗搜索博弈论
设 dp(who, cnt, x1, y1, x2, y2) 表示
在 who 走(who=0时为白子,who=1时为白子)这一步时
共走了cnt步, (x1,y1)为白子坐标,(x2,y2)为黑子坐标
如果不合法或平局或cnt>3n时,为inf 否则为到结束的步数
做法:
- 相邻则先手胜
- 先手拖延时间
后手尽快击败先手
点击查看代码
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <utility>
#include <queue>
using namespace std;
typedef signed char SC;
const int dx[] = {-1, 0, 1, 0};
const int dy[] = {0, 1, 0, -1};
int n, X1, Y1, X2, Y2;
int f[2][66][22][22][22][22];
// 对抗搜索
int dp(SC who, SC cnt, SC x1, SC y1, SC x2, SC y2) {
int &res = f[who][cnt][x1][y1][x2][y2];
if(res != -1) return res;
if(cnt > 3 * n) return res = 0x3f3f3f3f;
if(x1 == x2 && y1 == y2) return res = (who ? 0x3f3f3f3f : 0);
if(who) {
res = 0x3f3f3f3f;
for(int i = 0; i < 4; i ++) {
for(int d = 0; d < 2; d ++) {
int hx = x2 + (dx[i] << d), hy = y2 + (dy[i] << d);
if(hx >= 1 && hx <= n && hy >= 1 && hy <= n) res = min(res, dp(0, cnt + 1, x1, y1, hx, hy) + 1);
}
}
} else {
for(int i = 0; i < 4; i ++) {
int hx = x1 + dx[i], hy = y1 + dy[i];
if(hx >= 1 && hx <= n && hy >= 1 && hy <= n) res = max(res, dp(1, cnt + 1, hx, hy, x2, y2) + 1);
}
}
return res;
}
int main() {
scanf("%d%d%d%d%d", &n, &X1, &Y1, &X2, &Y2);
if(abs(X1 - X2) + abs(Y1 - Y2) == 1) puts("WHITE 1");
else {
for(int who = 0; who < 2; who ++) for(int i = 0; i <= 3 * n; i ++)
for(int x1 = 1; x1 <= n; x1 ++) for(int y1 = 1; y1 <= n; y1 ++)
for(int x2 = 1; x2 <= n; x2 ++) for(int y2 = 1; y2 <= n; y2 ++)
f[who][i][x1][y1][x2][y2] = -1; // 没有计算过
printf("BLACK %d\n", dp(0, 0, X1, Y1, X2, Y2));
}
return 0;
}
[ZJOI2009]P2599 取石子游戏
l[i][j] 表示在 i 左边放多少个石头, 使先手必败
- 存在 2. 唯一(反证) 存在:DP,分类讨论
点击查看代码
#include <stdio.h>
const int N = 1005;
int n, a[N], l[N][N], r[N][N];
int main() {
int T;
scanf("%d", &T);
while(T --) {
scanf("%d", &n);
for(int i = 1; i <= n; i ++) scanf("%d", a + i);
for(int len = 1; len <= n; len ++)
for(int i = 1, j; (j = i + len - 1) <= n; i ++)
if(len == 1) l[i][j] = r[i][j] = a[i];
else {
int L = l[i][j - 1], R = r[i][j - 1], X = a[j];
if(R == X) l[i][j] = 0;
else if((X < L && X < R) || (X > L && X > R)) l[i][j] = X;
else if(L > R) l[i][j] = X - 1;
else l[i][j] = X + 1;
L = l[i + 1][j], R = r[i + 1][j], X = a[i];
if(L == X) r[i][j] = 0;
else if((X < L && X < R) || (X > L && X > R)) r[i][j] = X;
else if(R > L) r[i][j] = X - 1;
else r[i][j] = X + 1;
}
if(n == 1) puts("1");
else printf("%d\n", int(l[2][n] != a[1]));
}
return 0;
}