博弈论

这真的是很神奇呢

注:这里引用了大量他人的博客,再次不一一说明,主要是为了学习

定义

博弈论又被称为对策论(Game Theory)既是现代数学的一个新分支,也是运筹学的一个重要学科。

博弈论主要研究公式化了的激励结构间的相互作用。是研究具有斗争或竞争性质现象的数学理论和方法。 博弈论考虑游戏中的个体的预测行为和实际行为,并研究它们的优化策略。生物学家使用博弈理论来理解和预测进化论的某些结果。

博弈论已经成为经济学的标准分析工具之一。在生物学、经济学、国际关系、计算机科学、政治学、军事战略和其他很多学科都有广泛的应用。

基本概念中包括局中人、行动、信息、策略、收益、均衡和结果等。其中局中人、策略和收益是最基本要素。局中人、行动和结果被统称为博弈规则。


引入

我们就用鼎鼎大名的Nim游戏来进行讲解

Nim游戏-传送门

那我现在就进行简单的说明吧

我会尽量用通俗易懂的话来说明

题目大意:

总有n堆石子,每堆有若干个石子

从A开始每次轮流从一堆石子中抽出若干个(可以全抽)

但是不能不拿

要不然没完没了。。。

看最后轮到谁没有石子可以抽,那么他就输了

并且两个人都聪明绝顶

解析:

我们可以先手模一下发现

如果只有一堆那么先手一定赢

如果剩下两堆不相等的石子,必胜策略是通过取多的一堆的石子将两堆石子变得相等,以后如果对手在某一堆里拿若干颗,你就可以在另一堆中拿同样多的颗数,直至胜利。

如果你面对的是两堆相等的石子,那么此时你是没有任何必胜策略的,反而对手可以遵循上面的策略保证必胜。

但是三个的话就要考虑更多的情况

我们定义P局面和N局面

P局面表示上一次移动的人必胜的局面(即后手必胜)

N局面表示现在移动的人必胜的局面(即先手必胜)

我们可以理解为:

1.无法进行任何移动的局面(也就是结束)是P;

2.可以移动到P的局面是N;

3.所有移动都导致N的局面是P。

好像有些难以理解?

好那让我再简单说一下

1代表的意思就是先手必败;

2代表的是移动到P的局面之前一定是N;

3代表的是移动到N的局面之前一定是P;

这样的话,如果在不重复的前提下

那么每个局面我们一定是可以计算出来的

比如刚才说当只有两堆石子且两堆石子数量相等时后手有必胜策略,也就是这是一个P,下面我们依靠定义证明一下(3,3)是一个P是一个P是一个P首先(3,3)的子局面(也就是通过合法移动可以导致的局面)有(0,3)(1,3)(2,3)(显然交换石子堆的位置不影响其性质,所以把(x,y)和(y,x)看成同一种局面),只需要计算出这三种局面的性质就可以了。(0,3)的子局面有(0,0)、(0,1)、(0,2),其中(0,0)显然是P,所以(0,3)是N(只要找到一个是P的子局面就能说明是N)(1,3)的后继中(1,1)是P(因为(1,1)的唯一子局面(0,1)是N),所以(1,3)也是N。同样可以证明(2,3)是N。所以(3,3)的所有子局面都是N,它就是P。再通过一点简单的数学归纳,可以严格的证明“有两堆石子时的局面是P当且仅当这两堆石子的数目相等”。

好的现在我们来总结一下:

根据定义,证明一种判断position的性质的方法的正确性,只需证明三个命题:

1、这个判断将所有结束局面判为P;

2、根据这个判断被判为N的局面一定可以移动到某个P;

3、根据这个判断被判为P的局面无法移动到某个P。

第一个命题显然,结束局面只有一个,就是全0,则^起来也是0

第二个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an!=0,一定存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。不妨设a1^a2^...^an=k,则一定存在某个ai,它的二进制表示在k的最高位上是1(否则k的最高位那个1是怎么得到的)。这时ai^k<ai一定成立。则我们可以将ai改变成ai'=ai^k,此时a1^a2^...^ai'^...^an=a1^a2^...^an^k=0。

第三个命题,对于某个局面(a1,a2,...,an),若a1^a2^...^an=0,一定不存在某个合法的移动,将ai改变成ai'后满足a1^a2^...^ai'^...^an=0。因为异或运算满足消去率,由a1^a2^...^an=a1^a2^...^ai'^...^an可以得到ai=ai'。所以将ai改变成ai'不是一个合法的移动。

那么我们就可以得出一个结论:

对于一个Nim游戏的局面(a1,a2,...,an),它是P当且仅当a1^a2^...^an=0

然后上代码!!!

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

const int maxn=10001000;
int t;
int n;
int a[maxn];

int main(){
	cin>>t;
	while(t--){
		cin>>n;
		int sum=0;
		for(int i=1;i<=n;i++) cin>>a[i];
		for(int i=1;i<=n;i++) sum^=a[i];
		if(sum==0) cout<<"No\n";
		else cout<<"Yes\n";
	}
}
01-01 10:09