博弈论--sg函数
博弈论
mex
首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。
例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
sg函数
定义P-position和N-position,其中P代表Previous,N代表Next。
直观的说,先手必败的局面是P-position,先手必胜的局面是N-position,
定义sg(x)=mex{ sg(y) | y是x的子状态 },也就是说y可以由x转移过去。
若x的所有子状态y都是必胜态,那么x就是必败态。
若x的所有子状态y有一个必败态,那么x就是必胜态。
如何求sg函数
打表
#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define pii pair<int,int>
#define int long long
const int inf = 0x3f3f3f3f;
const int maxn = 1110;
const int M = 1e9+7;
int n;
int a[maxn]; //每次移动可以选取的值
int sg[maxn];
bool vis[maxn];
void getsg()
{
for(int i = 1; i <= n; i++)
{
mem(vis,0);
for(int j = 1; a[j] <= i; j++) //可以取的值
{
vis[sg[i-a[j]]] = 1; //(i-a[j])状态可达
}
int res = 0;
for(;;res++)
{
if(!vis[res]) break;
}
sg[i] = res;
}
}
signed main()
{
cin>>n;
sg[0] = 0; //0表示必败
for(int i = 1; i <= n; i++)
{
a[i] = i*i;
}
getsg();
for(int i = 1; i <= n; i++)
{
cout<<sg[i]<<' ';
}
cout<<endl;
return 0;
}
dfs
int a[maxn]; //可以取的值
int sg[maxn];
int dfs_sg(int x)
{
if(sg[x] != -1) return sg[x];
int i; //必须要声明一下
bool vis[maxn]; //每次dfs对应不同的vis
mem(vis,0);
for(int i = 1; i <= n; i++)
{
if(x - a[i] >= 0)
{
vis[dfs_sg(x-a[i])] = 1;
}
}
int res = 0;
for(;;res++)
{
if(!vis[res]) break;
}
return sg[x] = res;
}
简化版本的sg
bool winnerSquareGame(int n) {
vector<int> v;
for(int i = 1; i*i <= n; i++)
{
v.push_back(i*i);
}
vector<int> sg(n+1); //只有两种值,0或1,0代表必败
for(int i = 1; i <= n; i++)
{
for(auto j : v)
{
if(j <= i)
{
if(!sg[i-j]) //如果子状态有必败,那么我必胜
{
sg[i] = 1;
break;
}
}
else break;
}
}
return sg[n];
}