博弈论——巴什博弈、威佐夫博弈、尼姆博弈

博弈论是二人在平等的对局中各自利用对方的策略变换自己的对抗策略,达到取胜的目的。
个人观点:博弈论的本质就是思考哪些是必胜或者必败的状态,其他不定的状态如何向这两种状态转化。

巴什博弈

只有一堆n个物品,两个人轮流从这堆物品中取物, 规定每次至少取一个,最多取m个。最后取光者得胜。

  • m ≥ n m\geq n mn 时,显然先手获胜
  • m < n m< n m<n 时,不难发现 ( m + 1 ) k , k = 0 , 1 , . . (m+1)k,k=0,1,.. (m+1)kk=0,1,.. 是必败的,其余状态都是必胜。

分析:当此时有 ( m + 1 ) k (m+1)k (m+1)k个,先手可以取 s s s个, s s s属于 1 − m 1-m 1m。对手一定可以取 ( m + 1 ) − s (m+1)-s (m+1)s个,那么这一轮结束后还有 ( m + 1 ) ( k − 1 ) (m+1)(k-1) (m+1)(k1)个,最后一轮还剩 m + 1 m+1 m+1个,无论先手取几,后手都可以取完。所以 ( m + 1 ) k , k = 0 , 1 , . . (m+1)k,k=0,1,.. (m+1)kk=0,1,.. 是必败的。
而其他的状态可以用 ( m + 1 ) k + r , k = 0 , 1 , . . , r < k (m+1)k+r,k=0,1,.., r < k (m+1)k+rk=0,1,..,r<k 来表示。那么先手只需要取 r r r个,后手就进入了必败态,所以先手必胜。

eg: HDU - 2188

#include
using namespace std;
string s1 = "Grass", s2 = "Rabbit";
int main(){
	int t;
	cin >> t;
	while(t--){
		int n, m;
		cin >> n >> m;
		if(n <= m) cout << s1 << endl;
		else if(n % (m + 1) == 0) cout << s2 << endl;
		else cout << s1 << endl;
	}
	return 0;
}

巴什博弈应该是最简单的。相关例题还有:HDU 1079 、HDU 1525 、HDU 1846 、HDU 1564 、HDU 1847 、HDU 2147 、HDU 2897 、HDU 2149 、POJ 2368 、HDU 1517

威佐夫博弈

有两堆各n个物品,两个人轮流从任意一堆中取出至少一个或者同时从两堆中取出同样多的物品,规定每次至少取一个,至多不限,最后取光者胜利

相关的推法就不推了,可以去看百度百科

直接说结论
用( a [ k ] , b [ k ] a[k],b[k] a[k]b[k])( a [ k ] ≤ b [ k ] , k = 0 , 1 , 2 , . . . , n a[k] ≤ b[k] ,k=0,1,2,...,n a[k]b[k],k=012...,n)(表示两堆物品的数量并称其为局势,如果甲面对 ( 0 , 0 ) (0,0) 00,那么甲已经输了,这种局势我们称为奇异局势。前几个奇异局势是: ( 0 , 0 ) (0,0) 00 ( 1 , 2 ) (1,2) 12 ( 3 , 5 ) (3,5) 35 ( 4 , 7 ) (4,7) 47 ( 6 , 10 ) (6,10) 610 ( 8 , 13 ) (8,13) 813
可以观察出个必败态的最大值减最小值是递增的自然数
0 − 0 = 0 0 - 0 = 0 00=0 2 − 1 = 1 2 - 1 = 1 21=1 5 − 3 = 2 5 - 3 = 2 53=2 7 − 4 = 3 7 - 4 = 3 74=3等等等,于是有如下结论

  • 任何自然数都包含在一个且仅有一个奇异局势中。
  • 任意操作都可将奇异局势变为非奇异局势。
  • 采用适当的方法,可以将非奇异局势变为奇异局势。

两个人如果都采用正确操作,那么面对非奇异局势,先拿者必胜;反之,则后拿者取胜。
那么任给一个局势(a,b),怎样判断它是不是奇异局势呢?我们有如下公式:
a k = [ k ( 1 + √ 5 ) / 2 ] a_k =[k(1+√5)/2] ak=[k1+5/2] b k = a k + k b_k= a_k + k bk=ak+k (方括号表示取整函数)
奇妙的是其中出现了黄金分割数 ( 1 + √ 5 ) / 2 = 1.618... (1+√5)/2 = 1.618... 1+5/2=1.618...因此,由 a k , b k a_k,b_k akbk组成的矩形近似为黄金矩形,由于 2 / ( 1 + √ 5 ) = ( √ 5 − 1 ) / 2 2/(1+√5)=(√5-1)/2 2/1+5=51/2,可以先求出 j = [ a ( √ 5 − 1 ) / 2 ] j=[a(√5-1)/2] j=[a51/2],若 a = = [ j ( 1 + √ 5 ) / 2 ] a==[j(1+√5)/2] a==[j1+5/2],那么 a = a j a = a_j a=aj b j = a j + j b_j = a_j + j bj=aj+j,若不等于,那么 a = a j + 1 a = a_j+1 a=aj+1 b = a j + j + 1 b = a_j + j + 1 b=aj+j+1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。

eg:HDU 1527

#include
using namespace std;
int main(){
	int n, m, a, b, t;
	double c, r;
	while(cin >> n >> m) {
		a = min(n, m);
		b = max(n, m);
		r = (sqrt(5.0) + 1) / 2;
		c =  double(b - a);
		t = int(r * c);
		if(t == a) cout << 0 << endl;
		else {
			cout << 1 << endl;
		}
	}
	return 0;
}

威佐夫博弈比巴什博弈难一点,但是套公式还是可以解决的。相关例题还有HDU - 2177、POJ - 1067

尼姆博弈

有若干堆石子,每堆石子的数量是有限的,二个人依次从这些石子堆中拿取任意的石子,至少一个(不能不取),最后一个拿光石子的人胜利。

三堆 ,我们用 ( a , b , c ) (a,b,c) a,b,c表示某种局势,首先 ( 0 , 0 , 0 ) (0,0,0) 0,0,0显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是 ( 0 , n , n ) (0,n,n) 0,n,n,只要与对手拿走一样多的物品,最后都将导致 ( 0 , 0 , 0 ) (0,0,0) 0,0,0。仔细分析一
下, ( 1 , 2 , 3 ) (1,2,3) 1,2,3也是奇异局势,无论对手如何拿,接下来都可以变为 ( 0 , n , n ) (0,n,n) 0,n,n的情型。
这里引入L . Bouton在1902年给出的定理:状态 ( x 1 , x 2 , x 3 ) (x_1,x_2,x_3) x1,x2,x3为必败状态当且仅当 x 1 x_1 x1 Xor x 2 x_2 x2 Xor x 3 x_3 x3 = 0,这里的Xor是二进制的逐位异或操作,也成Nim和。
在必胜局面下,因为所有数按位异或的结果是大于零的,那么通过一次取,将这个(大于其它所有数按位异或的结果的)数下降到其它所有数按位异或的结果,这时局面就变为必败局面了

eg: POJ - 2234

#include
#include
using namespace std;
string s1 = "Yes", s2 = "No";
int arr[50];
int main(){
	int n, m, a, b, t;
	double c, r;
	while(cin >> n) {
		int sum = 0;
		for(int i = 0; i < n; i++) cin >> arr[i], sum ^= arr[i];
		if(sum == 0) cout << s2 << endl;
		else cout << s1 << endl;
	}
	return 0;
}

尼姆博弈比较难,有很多变形,需要大量练习。相关例题还有POJ - 2975、POJ - 1704、HDU - 1850、HDU - 1907

你可能感兴趣的:(特性及算法)