博弈论总结

Posted Harris-H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了博弈论总结相关的知识,希望对你有一定的参考价值。

博弈论总结

在这里插入图片描述

博弈图中的状态

在这里插入图片描述

关于Nim和的证明

在这里插入图片描述

SG定理

在这里插入图片描述

用SG定理证明Nim和的结论

在这里插入图片描述

来自某大佬的总结

在这里插入图片描述


习题

P4101 [HEOI2014]人人尽说江南好

整体考虑,注意到答案取决于合并次数的奇偶性,则考虑需要合并多少次。

显然最终结果必然是: m , m , m … , n ( m o d m ) m,m,m\\dots,n\\pmod m m,m,m,n(modm)

合并的次数即为: n − ⌈ n m ⌉ n-\\lceil\\dfrac{n}{m}\\rceil nmn

P2575 高手过招

阶梯Nim+子游戏合并好题。

把一个空格两边的连续黑棋子看作一个台阶。

然后最右边加一个空格,相当于问题变成有若干层台阶,每次可以移动一个台阶的若干石子到右边的台阶,最后所有的棋子会到最右边的空格。

然后就裸了。注意台阶可以没有棋子,即多个空格不能算成一个空格。

因为行之间不会相互影响,所以相当于玩若干个子游戏。结果异或起来就可以了。

code

// Problem: P2575 高手过招
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2575
// Memory Limit: 125 MB
// Time Limit: 1000 ms
// Date: 2021-07-09 10:07:35
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define ios ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int sg(){
	int m;scanf("%d",&m);
	int s=0,f=0;
	int ans=0;
	int a[22]={};
	for(int i=1;i<=m;i++){
		int x;scanf("%d",&x);a[x]=1;
	}
	for(int i=20;~i;i--){
		if(a[i]) s++;
		else {
			if(f) ans^=s;
			s=0;
			f^=1;
		} 
	}
	return ans;
}
int main(){
	int t;scanf("%d",&t);
	while(t--){
		int n;scanf("%d",&n);
		int ans=0;
		for(int i=1;i<=n;i++){
			ans^=sg();
		}
		puts(ans?"YES":"NO");
	}
	return 0;
}

P2964 [USACO09NOV]A Coin Game S

经典博弈论dp,因为当前选取的情况取决于上一个人选取的个数,不妨设

d p [ i ] [ j ] dp[i][j] dp[i][j]当前剩下 i i i个硬币,上一个人取了 j j j个的答案。

注意到 d p [ i ] [ j ] dp[i][j] dp[i][j]包含 d p [ i ] [ j − 1 ] dp[i][j-1] dp[i][j1],因为上一个取更多答案不会更劣。

所以我们只需考虑转移 k = 2 j − 1 , 2 j k=2j-1,2j k=2j1,2j这两种情况。

a i a_i ai是从右往左的前缀和。

d p [ i ] [ j ] = m a x { d p [ i ] [ j − 1 ] , s [ i ] − d p [ i − k ] [ k ] , s [ i ] − d p [ i − ( k + 1 ) ] [ k + 1 ] } dp[i][j]=max\\{dp[i][j-1],s[i]-dp[i-k][k],s[i]-dp[i-(k+1)][k+1]\\} dp[i][j]=max{dp[i][j1],s[i]dp[ik][k],s[i]dp[i(k+1)][k+1]}

时间复杂度: O ( n 2 ) O(n^2) O(n2)

	for(int i=1;i<=n;i++)
		for(int j=1,k=2*j-1;j<=n;j++,k+=2){
			dp[i][j]=dp[i][j-1];
			if(k<=i) dp[i][j]=max(dp[i][j],a[i]-dp[i-k][k]);
			if(k+1<=i) dp[i][j]=max(dp[i][j],a[i]-dp[i-k-1][k+1]);
		}

P2734 [USACO3.3]游戏 A Game

经典博弈论dp,类似区间dp。

	for(int i=1;i<=n;i++) a[i]+=a[i-1];
	for(int k=1;k<=n;k++)
		for(int l=1,r=l+k-1;r<=n;l++,r++){
			f[l][r]=max(f[l][r],a[r]-a[l-1]-f[l+1][r]);
			f[l][r]=max(f[l][r],a[r]-a[l-1]-f[l][r-1]);
	}

P5675 [GZOI2017]取石子游戏

经典Nim套个dp。

显然限制了第一堆的选取后,游戏的胜负取决于其他堆的石子的异或和 s u m sum sum

显然 s u m ≥ a i sum\\ge a_i sumai ,不然先手总可以通过拿走第一堆石子的若干个使得异或和为0,这样后手就输了。

因此可以设置状态: f [ i ] [ j ] f[i][j] f[i][j] i i i堆石子异或和为 j j j的方案数。

然后就正常dp。

void dp(int x){
	for(int i=1;i<=n;i++)
		for(int j=0;j<N;j++){
			f[i][j]=(i==x)?f[i-1][j]:(f[i-1][j]+f[i-1][j^a[i]])%mod;
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	f[0][0]=1;
	for(int i=1;i<=n;i++){
		dp(i);
		for(int j=a[i];j<N;j++)
			ans=(ans+f[n][j])%mod;
	}
	printf("%lld\\n",ans);
	return 0;
}

SG函数习题二刷

Fibonacci again and again

以fibnacci 的前几项为状态转移,暴力跑sg即可。

void solve(){
	bitset<N>mex;
	for(int i=1;i<N;i++){
		mex.reset();
		int j;
		for(j=1;j<=15&&f[j]<=i;j++)
			mex[sg[i-f[j]]]=1;
		for(j=0;;j++)
			if(!mex[j]) break;
		sg[i]=j;
	}
}
int main(){
	f[1]=f[2]=1;for(int i=3;i<=15;i++) f[i]=f[i-1]+f[i-2];
	while(~scanf("%d%d%d",&n,&m,&p)){
		if(!n&&!m&&!p) break;
		solve();
		puts((sg[n]^sg[m]^sg[p])?"Fibo":"Nacci");
	}
	return 0;
}

HDU 1536 S-Nim

SG板子水题,注意先对状态转移数组排序

// Problem: S-Nim
// Contest: Virtual Judge - LibreOJ
// URL: https://vjudge.net/problem/LibreOJ-10247
// Memory Limit: 524 MB
// Time Limit: 1000 ms
// Date: 2021-07-09 15:05:18
// --------by Herio--------

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull; 
const int N=1e4+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define IOS ios::sync_with_stdio(false),cin.tie(0) 
void Print(int *a,int n){
	for(int i=1;i<n;i++)
		printf("%d ",a[i]);
	printf("%d\\n",a[n]); 
}
int n,m,k;
int f[N],sg[

以上是关于博弈论总结的主要内容,如果未能解决你的问题,请参考以下文章

简单博弈论总结

专题总结(博弈论)

机器博弈中的数据结构与基本方法-----总结

博弈论知识点总结

算法总结博弈论相关

博弈论题目总结——组合游戏