# 博弈论入门（Game Theory）

### 一、巴什博弈（Bash Game）

n=k * (m+1)+r

#### 代码：

int main(){
LL t;sf(t);
while(t--){
LL n,m;sf(n),sf(m);
if(n%(m+1))printf("first\n");//可以把k*(m+1)这个状态给对面
else printf("second\n");//到自己手上的状态是k*(m+1)
}
}


#### 例题：

##### 1.Brave Game

HDU - 1846

1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个；
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子；
5、 最先取光石子的一方为胜；

Input

Output

Sample Input
2
23 2
4 3
Sample Output
first
second

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;

int main()
{
int T,n,m;
cin>>T;
while(T--){
cin>>n>>m;
int l=n%(m+1);
if(l) cout<<"first"<<endl;
else cout<<"second"<<endl;
}
}

##### 2. kiki’s game

HDU - 2147
Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes the checkerboard game.The size of the chesserboard is n*m.First of all, a coin is placed in the top right corner(1,m). Each time one people can move the coin into the left, the underneath or the left-underneath blank space.The person who can’t make a move will lose the game. kiki plays it with ZZ.The game always starts with kiki. If both play perfectly, who will win the game?
Input
Input contains multiple test cases. Each line contains two integer n, m (0<n,m<=2000). The input is terminated when n=0 and m=0.
Output
If kiki wins the game printf “Wonderful!”, else “What a pity!”.

Sample Input
5 3
5 4
6 6
0 0
Sample Output
What a pity!
Wonderful!
Wonderful!

Tips： 只要m或者n有一个是偶数先手就能必胜。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;

int main( )
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(m==0||n==0) break;
if(n%2==0||m%2==0)
printf("Wonderful!\n");
else
printf("What a pity!\n");
}
}


### 二、威佐夫博弈（Wythoff Game）

#### 代码：

int main(){
int a,b;
while(scanf("%d%d",&a,&b)!=EOF){
if(a>b)swap(a,b);
int k=b-a;
int ans=(a!=floor(k*(1.0+sqrt(5.0))/2));
printf("%d\n",ans);//0必败态 1必胜态
}
return 0;
}


#### 例题：

##### 1.取石子游戏

HDU - 1527

Input

Output

Sample Input
2 1
8 4
4 7
Sample Output
0
1
0

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;

int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
if(n>m) swap(n,m);
int l=(m-n)*(1.0*(sqrt(5)+1)/2);
if(n==l) printf("0\n");
else printf("1\n");
}
}


### 三、尼姆博弈论（Nim Game）

#### 操作：

a1⊕a2⊕a3⊕…⊕ak ==0，先手胜。
a1⊕a2⊕a3⊕…⊕ak !=0，先手败。

.
(1)必定能够从N- position转化到P-position.也就是说，先手处于必胜点N-position时可以拿走一一些石子，让后手必败。

.
(2)进人P-position后，轮到的下一个玩家，不管拿多少石子都会转移到N-position,因为任何一堆的数量变化,都会使得这堆的进制数至少有一位发生变化，导致异或运算的结果不等于0。也就是说,这一个玩家不管怎么拿石子都必败。

.
(3)在游戏过程中，按上述(1)和(2)的步骤在Nposition和P-position之间交替转化，直到所有堆的石头都是0,即终止于P-position。

——《算法竞赛入门到进阶》

#### 代码：

int main(){
int m,ans,n;
while(~scanf("%d",&m)){
n=ans=0;
while(m--)
scanf("%d",&n),ans^=n,printf("ans=%d\n",ans);
if(ans)printf("Yes\n");
else printf("No\n");
}
}


#### 例题：

##### 1. Being a Good Boy in Spring Festival

HDU - 1850

——“先手的人如果想赢，第一步有几种选择呢？”
Input

Output

Sample Input
3
5 7 9
0
Sample Output
1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;

const int N = 100;
int a[N];

int main()
{
int n;
while(~scanf("%d", &n) && n)
{
int ans = 0;
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
ans^=a[i];
}
if(ans==0)
printf("0\n");
else {
int cnt=0;
for(int i=0;i<n;i++)
if((ans^a[i])<a[i]) cnt++;
printf("%d\n",cnt);
}
}
}


### 四、斐波那契博弈论

1)先手不能在第一次把所有的石子取完；
2)之后每次可以取的石子数介于1到对手刚取的石子数的2倍之间（包含1和对手刚取的石子数的2倍）。

#### 代码：

int f[1000];
int F(int n)
{
f[1]=1;f[2]=1;
for(int i=3;i<=100;i++)
{
f[i] = f[i-1]+f[i-2];
if(f[i]==n)return 1;
if(f[i]>n)break;
}
return 0;
}


#### 例题：

##### 1. 取石子游戏

HDU - 2516
1堆石子有n个,两人轮流取.先取者第1次可以取任意多个，但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
Input

Output

Sample Input
2
13
10000
0
Sample Output
Second win
Second win
First win

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;

int f[1000];
int F(int n)
{
f[1]=1;f[2]=1;
for(int i=3;i<=100;i++)
{
f[i] = f[i-1]+f[i-2];
if(f[i]==n)return 1;
if(f[i]>n)break;
}
return 0;
}
int main()
{
int n;
while(cin>>n)
{
if(n==0)break;
if(F(n)==0)
printf("First win\n");
else
printf("Second win\n");
}
}


### 五、SG函数

#### 代码：

void getSG(int n){
memset(sg,0,sizeof sg);
for(int i=1;i<=n;i++){               //n个物品
memset(vis,0,sizeof vis);
for(int j=0;j<=m&&i-j>0;j++)
vis[sg[i-j]]=1;              //j表示可以取的数
for(int j=0;j<=n;j++)
if(!vis[j]){
sg[i]=j;
break;
}
}
}


#### 例题：

##### 1. Good Luck in CET-4 Everybody!

HDU - 1847

“升级”？“双扣”？“红五”？还是“斗地主”？

1、 总共n张牌;
2、 双方轮流抓牌；
3、 每人每次抓牌的个数只能是2的幂次（即：1，2，4，8，16…）
4、 抓完牌，胜负结果也出来了：最后抓完牌的人为胜者；

Good luck in CET-4 everybody!
Input

Output

Sample Input
1
3
Sample Output
Kiki
Cici

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;

const int N=1010;
int sg[N],f[11];
bool vis[N];

void getSG(int n)
{
memset(sg,0,sizeof sg);
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof vis);
for(int j=0;f[j]<=i;j++)
vis[sg[i-f[j]]]=1;
for(int j=0;j<=n;j++)
if(!vis[j])
{
sg[i]=j;
break;
}
}
}
int main()
{
int n;
for(int i=0;i<=10;i++) f[i]=(1<<i);
getSG(1000);
while(~scanf("%d",&n)&&n)
{
if(sg[n]) printf("Kiki\n");
else printf("Cici\n");
}
}

##### 2. S-Nim

POJ - 2960
Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows:
The starting position has a number of heaps, all containing some, not necessarily equal, number of beads.
The players take turns chosing a heap and removing a positive number of beads from it.
The first player not able to make a move, loses.
Arthur and Caroll really enjoyed playing this simple game until they
recently learned an easy way to always be able to find the best move:
Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1).
If the xor-sum is 0, too bad, you will lose.
Otherwise, move such that the xor-sum becomes 0. This is always possible.
It is quite easy to convince oneself that this works. Consider these facts:
The player that takes the last bead wins.
After the winning player’s last move the xor-sum will be 0.
The xor-sum will change after every move.
Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win.

Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S = {2, 5} each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it?

your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.
Input
Input consists of a number of test cases.
For each test case: The first line contains a number k (0 < k ≤ 100) describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps.
The last test case is followed by a 0 on a line of its own.
Output
For each position: If the described position is a winning position print a ‘W’.If the described position is a losing position print an ‘L’.
Print a newline after each test case.
Sample Input
2 2 5
3
2 5 12
3 2 4 7
4 2 3 7 12
5 1 2 3 4 5
3
2 5 12
3 2 4 7
4 2 3 7 12
0
Sample Output
LWW
WWL

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<cmath>
#include<string>
using namespace std;
int s[105],sg[10001];
bool vis[10001];
void getSG(int t)
{
int i,j;
memset(sg,0,sizeof sg );
for(i=1;i<=10001;i++)
{
memset(vis,0,sizeof vis );
for(j=1;j<=t&&s[j]<=i;j++)
vis[sg[i-s[j]]]=1;
for(j=0;j<=10001;j++)
if(!vis[j])
break;
sg[i]=j;
}
}
int main()
{
int k;
while(cin>>k,k)
{
for(int i=1;i<=k;i++)
cin>>s[i];
sort(s+1,s+k+1);
getSG(k);
int m,n,ans,t;
cin>>m;
while(m--)
{
cin>>n;
ans=0;
for(int i=0;i<n;i++)
{
cin>>t;
ans^=sg[t];
}
if(ans)
cout<<'W';
else
cout<<'L';
}
cout<<endl;
}
}


05-26 4094
08-16 4725
04-02 239
08-28 37
10-08 1851