码迷,mamicode.com
首页 > 其他好文 > 详细

博弈总结

时间:2020-11-02 10:50:17      阅读:37      评论:0      收藏:0      [点我收藏+]

标签:break   void   space   速度   ++   return   证明   总结   情况下   

这里是看了学长的例会以及挑战上关于博弈的知识进行一下知识总结

1、PN法

定义:
必败点P:位于此位置的人,在双方操作均优的情况下,必败,记作“0”
必胜点N:位于此位置的人,在双方操作均优的情况下,必胜,记作“1”

性质:
游戏结束的点(通常为0点)均为必败点
必胜点等价于必有一种方式可以到达必败点
必败点等价于所能到的点全为必胜点

2、巴什博弈

\(n\)个石子,A B两个人取,操作者必须取\(1\sim m\)个石子,先取完获胜
结论:必败点 \(n\%\left(m+1\right)=0\) ? 必胜点\(n\%\left(m+1\right)\ne0\)
证明:(假设\(m=2\)

n 0 1 2 3 4 5 6 7 8 9
PN 0 1 1 0 1 1 0 1 1 0

3、SG定理

  1. 局面的异或:对于平行关系下(也就是一个局面下)的\(sg\)值是所有局面下\(sg\)值的异或
    \(sg\left(n\right)= sg(s_1) \oplus sg(s_2) \oplus ...\oplus sg(s_n)\)
  2. \(mex\)运算:\(mex(A)\)代表最小的不属于A集合的自然数
    \(mex(A)=\{x|x\in\N \wedge\neg \left(\exist y\right)\left( y\in A \wedge y<x\right)\}\)
  3. \(sg(n)=0\)时,必败;\(sg(n)\ne0时\),必胜
  4. 证明方法运用了类比(必胜点的转移和数字的异或是否为0具有相似性)

一个其实用处不大的模板
博主实测,\(mex\)使用vis数组速度远快过set

int sg[maxn],f[maxn];
bool vis[maxn];
void get_sg()
{
	memset(sg, 0, sizeof(sg));
	for (int i = 1; i < maxn; ++i)
	{
		memset(vis,0,sizeof(vis));
		for (int j = 0; j < N && f[j] <= i; ++j)
			vis[sg[i - f[j]]];
		for(int j=0;;++j)
			if (!vis[j])
			{
				sg[i] = j;
				break;
			}
	}
}

4、SG定理的应用

洛谷-P2197 Nim博弈:\(n\)堆石子,每堆有\(a[i]\)个,A B两人轮流取,每次可以在一堆石子中取任意个(可一次性取完)最后率先取完者获胜
结论:直接套用SG定理,可以发现每堆石子的\(sg\)值恰好为拥有石子的个数,再求异或和即可

HDU-2873 Bomb Game
结论:利用记忆化搜索求得SG值

#include<bits/stdc++.h>
using namespace std;
int sg[55][55];
int get_sg(int x, int y)
{
	if (~sg[x][y])
		return sg[x][y];
	bool vis[3000] = {0};
	if (x > 1 && y > 1)
	{
		for (int i = 1; i < x; ++i)
			for (int j = 1; j < y; ++j)
				vis[get_sg(i, y)^get_sg(x, j)]=true;
	}
	else if (y == 1 && x > 1)
		for (int i = 1; i < x; ++i)
			vis[get_sg(i, 1)]=true;
	else if (x == 1 && y > 1)
		for (int i = 1; i < y; ++i)
			vis[get_sg(1, i)]=true;
	for (int i = 0;; ++i)
		if (vis[i] == false)
			return sg[x][y] = i;
}
char s[55];
int main()
{
	int n, m;
	memset(sg, -1, sizeof(sg));
	sg[1][1] = 0;
	while (~scanf("%d%d", &n, &m) && n + m)
	{
		int ans = 0;
		for (int i = 1; i <= n; ++i)
		{
			scanf("%s", s + 1);
			for (int j = 1; j <= m; ++j)
				if (s[j] == ‘#‘)
					ans ^= get_sg(i, j);
		}
		printf("%s\n", ans ? "John" : "Jack");
	}
}

博弈总结

标签:break   void   space   速度   ++   return   证明   总结   情况下   

原文地址:https://www.cnblogs.com/DreamW1ngs/p/13912492.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!