博弈论与SG函数

J. Alice and Bob-1

先看这个题,题意很简单,一个序列中,小A每次拿一个数,小B每次拿一个数,然后各自的得分为取得的数的和的绝对值。问两个人都采取最优策略,谁将获胜?
我们假设A为小A最后取得的数的和B同理,S为总的和,即小A的想法就是(ans=|A|-|B|)足够大,我们适当的代换一下这个式子,即(ans=|A|-|B|=|A|-|S-A|),这个时候可以进行分类讨论来将绝对值去掉。可以得到以下式子:
(f(x) = left{ egin{array}{lr} 2*A-S & : A>=0,S>=A\ S & : A>=0,S<A \ -S & : A<0,S>=A \ -2A+S & :A<0,S<A\ end{array} ight.)
之后可以根据S>0和S<0的情况再讨论,通过画图可以得出当S<0时,图是不增的,S>0时图是不减的。注意这个图像的含义,其中S是我们(小A和小B)无法控制的量,而他们能控制的就是他们各自的值即(A,B).通过观察A和ans的图像可以得到,当S<0时,小A只要使得自己的A值足够小就行了,所以他每次直接选剩下值最小的即可。而S>0时则相反,选最大的即可。小B与小A相同。

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=5010;
int n,a[N];
int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d",&n);
	ll sum=0;
	for(int i=1;i<=n;++i) scanf("%d",&a[i]),sum+=a[i];
	sort(a+1,a+n+1);
	ll A=0,B=0;
	if(sum>0)
	{
		int id=0;
		for(int i=n;i>=1;--i)
		{
			if(!id) A+=a[i];
			else B+=a[i];
			id^=1;
		}
	}
	else
	{
		int id=0;
		for(int i=1;i<=n;++i)
		{
			if(!id) A+=a[i];
			else B+=a[i];
			id^=1;
		}
	}
	if(A<0) A=-A;
	if(B<0) B=-B;
	printf("%lld",A-B);
	return 0;
} 

K. Alice and Bob-2

在淦这个题之前就需要一些知识做铺垫...
首先我们先了解什么是ICG公平游戏,与有向图游戏,这个可以自己百度。
最后得知有向图游戏的和为所有有向图游戏的异或。即可。这个题由于每个字符串的大小就40,我们直接暴力找每个字符串的SG函数即可。(记忆化+爆搜yyds)

#include<bits/stdc++.h>
#define ll long long 
using namespace std;
const int N=101;
int T,n,cnt;
char c[N];
map<vector<int>,int>SG;

inline bool check(vector<int>x)//检查是否到末节点。
{
	for(int i=0;i<=25;++i) if(x[i]) return false;
	return true;
} 

inline int find(vector<int>x)
{
	++cnt;
	if(SG.find(x)!=SG.end()) return SG[x]; 
	if(check(x)) return SG[x]=0;
	vector<int>v;
	for(int i=0;i<=25;++i)
	{
		if(x[i])
		{
			vector<int>y=x;
			y[i]--;
			sort(y.begin(),y.end());
			v.push_back(find(y));
			
		}
	}
	for(int i=0;i<=25;++i)
	{
		if(x[i])
		{
			for(int j=0;j<=25;++j)
			{
				if(i!=j&&x[j])
				{
					vector<int>y=x;
					y[i]--;y[j]--;
					sort(y.begin(),y.end());
					v.push_back(find(y));
				}
			}
		}
	}
	for(int i=0;;++i)
	{
		bool flag=false;
		for(auto zs:v) 
		{
			if(zs==i)
			{
				flag=true;
				break;
			}
		}
		if(!flag) return SG[x]=i;	
	}
}

int main()
{
//	freopen("1.in","r",stdin);
	scanf("%d",&T);
	vector<int>vs;
	for(int i=1;i<=26;++i) vs.push_back(0); 
	while(T--)
	{
		scanf("%d",&n);
		int ans=0;
		for(int i=1;i<=n;++i)
		{
			scanf("%s",c+1);
			for(int j=0;j<26;++j) vs[j]=0; 
			int num=strlen(c+1);
			for(int j=1;j<=num;++j) vs[c[j]-'a']++;
			sort(vs.begin(),vs.end());
			ans^=find(vs);
		}
		if(ans>0) puts("Alice");
		else puts("Bob");
	}
	return 0;
} 

更新一下,我自己之前的错误的观念。公平游戏的每个子游戏,我们不能仅看当前局面是否必胜就完事了,因为总的游戏的SG函数为各个子游戏的SG函数的异或和,所以我们必须求出每个子游戏的SG函数值才行。
定理:当且仅当当前的SG函数>0,当前游戏必胜,否则必输。

原文地址:https://www.cnblogs.com/gcfer/p/15441519.html