博弈論
理論鋪墊:
定義P-position和N-position:其中P代表Previous,N代表Next。直觀的說,上一次move的人有必勝策略的局面是P-position,即P-position代表先手必輸,N-position代表先手必贏。
1.公平組合博弈(ICG)
定義:
1.兩個人輪流參與; 2.游戲局面有限; 3.同一個局面兩個人操作完全相同; 4.無法進行操作的人輸; 5.在有限步內結束。
模型:給定一個有向無環圖和一個起始頂點上的一枚棋子,兩名選手交替的將這枚棋子沿有向邊進行移動,無法移動者判負。
解題思路:SG函數(Sprague-Grudy)
SG定理:
1:給定一個有限子集 S ⊂ N,令mex S為沒有出現在S中的最小自然數。例:mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。
2:對於一個給定的有向無環圖,定義關於圖的每個頂點的Sprague-Garundy函數g如下:sg(x)=mex{ sg(y) | y是x的后繼 }。即y可以一步操作到x;
SG函數性質:
1:SG值為0必敗態, 否則為必勝態
2:對於一個sg(x)=0的頂點x,它的所有后繼y都滿足g(y)!=0。同理,對於一個sg(x)!=0的頂點,必定存在一個后繼y滿足g(y)=0。
例:有三堆石子,第一堆石子的可以取1、2、3個石子,則:sg[x] = x % 4;第二堆可以取奇數個石子,則sg[x] = n % 2;第三堆有n個石子,其為Nim博弈,sg值為n,將三堆的sg值全部異或一下,判斷是不是0,是0則先手必敗(推廣到n堆其結論一樣適用)。
SG函數求法
其sg函數可以通過下面的模板來求:
//f[]:可以取走的石子個數
//sg[]:0~n的SG函數值
//hash[]:mex{}
int f[N],sg[N],hash[N];
void getSG(int n)
{
int i,j;
memset(sg,0,sizeof(sg));
for(i=1;i<=n;i++)
{
memset(hash,0,sizeof(hash));
for(j=1;f[j]<=i;j++)
hash[sg[i-f[j]]]=1;
for(j=0;j<=n;j++) //求mes{}中未出現的最小的非負整數
{
if(hash[j]==0)
{
sg[i]=j;
break;
}
}
}
}
記憶化搜索求SG函數,判斷該點的sg值是否為-1,如果是,則調用函數:
//注意 S數組要按從小到大排序 SG函數要初始化為-1 對於每個集合只需初始化1遍
//n是集合s的大小 f[i]是定義的特殊取法規則的數組
int s[110],sg[10010],n;
void SG_dfs(int x)
{
int i;
if(sg[x]!=-1)
return;
bool vis[maxn];
memset(vis,0,sizeof(vis));
for(i=1;i<=k;i++)
{
if(x>=f[i])
{
if(sg[x-f[i]]==-1)
SG_dfs(x-f[i]);
vis[sg[x-f[i]]]=1;
}
}
int e;
for(i=0;;i++)
if(!vis[i])
{
sg[x]=i;
break;
}
}
AC代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+5;
int f[101],sg[maxn];
int k,T,tot=0,temp,inf;
void SG_dfs(int x)
{
int i;
if(sg[x]!=-1)
return;
bool vis[maxn];
memset(vis,0,sizeof(vis));
for(i=1;i<=k;i++)
{
if(x>=f[i])
{
if(sg[x-f[i]]==-1)
SG_dfs(x-f[i]);
vis[sg[x-f[i]]]=1;
}
}
int e;
for(i=0;;i++)
if(!vis[i])
{
sg[x]=i;
break;
}
}
int main()
{
//ios::sync_with_stdio(false);
while(scanf("%d",&k)&&k)
{
//memset(f,0,sizeof(f));
memset(sg,-1,sizeof(sg));
sg[0]=0;
for(int i=1;i<=k;++i){
scanf("%d",&f[i]);
}
//sort(f+1,f+k+1);
scanf("%d",&T);
while(T--)
{
tot=0;
scanf("%d",&temp);
for(int i=1;i<=temp;++i){
scanf("%d",&inf);
if(sg[inf]==-1)
SG_dfs(inf);
tot^=sg[inf];
}
!tot?cout<<"L":cout<<"W";
}
cout<<endl;
}
system("pause");
return 0;
}
2.Bash(巴什博弈)
定義:有一堆共n個物品,兩個人輪流從中間取,每次最多取m個物品,先取完的人贏。
解題思路:n對(m+1)取模,模不為0,則先手必勝,模為0,則先手必敗(由SG函數知)。
3.Nim博弈(尼姆博弈)
定義:有n堆物品,兩人輪流從每堆中取物品,每次最少取一個,無上限,先取完者勝利。
解題思路:所有堆的個數相異或,如果結果為0,則先手必敗,不為0則先手必勝。必勝走法的方案數:將總的異或結果同每一堆相異或,如果結果小於該堆的數量,則可以從該堆取出x-x^tot個;
4.Wythoff Game(威佐夫博弈)
定義:有兩堆若干個物品,兩人輪流從一堆中取若干的物品或兩堆取同樣多的額物品,至少取一個,先取完的為勝。
解題思路:令當前局勢(Ak,Bk),則當前態勢為先手必敗態當且僅當Ak=((sqrt(5)+1)/2)*k、Bk=Ak+k;取出多少個則直接暴力循環判斷是不是必敗態即可
5.Fibonacci博弈
定義:有一堆個數為n的石子,雙方輪流取石子,先手不能一次把所有石頭取完,且后者取得石子的數目不能超過前者所取數目的2倍(可以包含1和2倍)。
解題思路:判斷該數是不是裴波那契數列,如果是,則先手必敗,否則先手必勝。