文章目录
前言
简单学了博弈论,推导难度大。先了解了几个经典的博弈论和P/N分析法。SG函数,打算后面再学。
定义
博弈论:是二人或多人在平等的对局中各自利用对方的策略变换自己的对抗策略,达到取胜目标的理论。博弈论是研究互动决策的理论。博弈论可以分析自己与对手的利弊关系,从而确立自己在博弈中的优势,因此有不少博弈理论,可以帮助对奕者分析局势,从而采取相应策略,最终达到取胜的目的。
在算法竞赛中博弈论往往有以下特征:
1.有两名选手
2.两名选手交替操作,每次一步,每步都是在有限的合法集合中选择一种进行
3.在任何情况下,合法操作只取决于情况本身,与选手无关
4.游戏选手败北条件:当某位选手进行操作时,当前没有任何可以执行的合法操作
巴什博弈(Bash Game)
一堆n个物品,两个人从轮流中取出(1~m)个,最后取光者胜(不能继续取的人输)
思路:同余定理n=k*(m+1)+r,先取者首先拿走r,那么后者无论拿走(1~m)多少,先者只需要拿和后者拿走的总数为m+1即可,那么先手必赢;否则先手必输。
裸题
#include<bits/stdc++.h>
using namespace std;
int t;
int main(){
scanf("%d",&t);
while(t--){
int n,m;scanf("%d%d",&n,&m);
if(n%(m+1)==0){
printf("second\n");
}
else printf("first\n");
}
}
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
scanf("%d",&t);
while(t--){
int m,n;scanf("%d%d",&m,&n);
int num=m%(n+1);
if(!num)printf("Rabbit\n");
else printf("Grass\n");
}
}
HDU 2149
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
while(cin>>m>>n){
if(n>=m){
for(int i=m;i<=n;i++)
if(i!=n)
printf("%d ",i);
else
printf("%d\n",i);
}
else {
int num=m%(n+1);
if(!num) printf("none\n");
else printf("%d\n",num);
}
}
}
斐波那契博弈(Fibonacci’s Game)
一堆n(n>=2)的石子,游戏双方轮流取石子,规则如下:
先手不能在第一次把石头取完,至少取1个
之后每一次取石头至少为1,至多为刚刚对手的二倍
取走最后一个石头的人为赢家,求必败态
当n等于Fibonacci时必败.我们把一堆石子看成k堆和k-1堆,后手取完k-1堆后,先手不能一下子取完k堆,后手仍可以取得最后一颗。
当n不等于Fibonacci时必胜.
齐肯多夫定理:任何正整数可以表示为若干个不连续的Fibonacci数之和
n=f[a1]+f[a2]+…+f[ap],a1>a2>…>ap,f[a(p-1)]>2*f[ap].我们令先手取完f[ap],后手只能选择f[a(p-1)],且不能取完。对于以后的每一堆,先手都可以取这一堆的最后一颗石子,从而获得游戏的胜利
裸题
#include<bits/stdc++.h>
using namespace std;
const int maxn=50;
int f[maxn+10];
void init(){
f[1]=2,f[2]=3;
for(int i=3;i<=50;i++) f[i]=f[i-1]+f[i-2];
}
int main(){
int n;init();
while(scanf("%d",&n)&&n){
bool flag=false;
for(int i=1;i<=50;i++){
if(n==f[i]){
flag=true;break;
}
}
if(flag) printf("Second win\n");
else printf("First win\n");
}
}
威佐夫博弈(Wythoff Game)
有两堆各若干个物品,两个人轮流从任何一堆中取出至少一个或者同时从两堆中取出同样多的物品,规定每次最少取一个,至多不限,最后取光者胜利
奇异局势:当甲面对奇异局势时,甲会输掉比赛。
所以面对非奇异局势,先手必胜;否则,后手胜
前几个奇异局势是:(0,0)、(1,2)、(3,5)、(4,7)、(6,10)、(8,13)、(9,15)、(11,18)、(12,20)…
可以看出ak是前面没有出现的最小自然数,bk=ak+k。具体求解公式:a[k]=k*(1+√5)/2,b[k]=k*(3+√5)/2
奇异局势的性质:
1.任何自然数都包含在一个且仅有的一个奇异局势中
2.任何操作都可以将奇异局势变为非奇异局势
3.采用适当操作可以将非奇异局势变为奇异局势
由Betty理论…
最后得出a=[øi],b=[(ø+1)i],其中ø=(sqrt(5)+1)/2为黄金分割比
裸题
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
using namespace std;
int main(){
int a,b,c;
while(cin>>a>>b){
if(a>b) swap(a,b);
c=(int)(b-a)*((sqrt(5.0)+1)/2);
if(a==c) printf("0\n");
else printf("1\n");
}
}
尼姆博弈(Nimm Game)
n堆物品,两人轮流取,每次取某堆取任意,至少一个,最后取完者胜。
结论:将n堆物品数量全部异或后结果为0则必败,否则必胜。
显然(0,0,0)、(0,n,n),(1,2,3)是奇异局势。
任何奇异局势a ^ b ^ c=0。
非奇异局势如何转化为奇异局势:c - a ^ b
裸题
若有三堆物品a b c ,a ^ b = ans ,如果c >= ans , 我们在c中抽出x个个物品使得a ^ b ^ (c -x ) = 0 ,就是一种方法。推广到n。
#include<bits/stdc++.h>
using namespace std;
const int maxn=200;
int a[maxn];
int main(){
int n;
while(scanf("%d",&n),n){
int ans=0,sum=0;
for(int i=1;i<=n;i++) scanf("%d",&a[i]),ans^=a[i];
for(int i=1;i<=n;i++){
if(a[i]>(ans^a[i])) sum++;
}
printf("%d\n",sum);
}
return 0;
}
画P/N分析
题意:一个n*m的方格,一枚硬币在(1,m),谁将硬币移动到(n,1)谁就赢。每次移动只能走左、下、左下。
思路:画出P/N分析图,也就是当前对手如果在(x,y),你是否可以保证赢,从(n,1)一点一点往上推即可
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
while(scanf("%d%d",&n,&m),n+m){
if(n%2&&m&1){
printf("What a pity!\n");
}
else printf("Wonderful!\n");
}
}
通过画P/N分析得,
1 2 3 4 5 6 7
N N P N N P N
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
while(scanf("%d",&n)!=EOF){
if(n%3==0) printf("Cici\n");
else printf("Kiki\n");
}
return 0;
}