博弈论小结

博弈

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 否则为到结束的步数
做法:

  1. 相邻则先手胜
  2. 先手拖延时间
    后手尽快击败先手
点击查看代码

#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 左边放多少个石头, 使先手必败

  1. 存在 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;
}
posted @ 2022-10-24 10:24  azzc  阅读(15)  评论(0编辑  收藏  举报