博弈论(Game Theory)

<font color=blue>博弈论(Game Theory)</font>

首先先说两个定义 N状态:<font color=red>前面的一个玩家</font>必胜 P状态: <font color=red>后面一个玩家</font>必胜c++

巴什博弈 (Bush Game)

有一堆数量为n的物体,轮流拿,至少拿1个,至多拿k个(N>K); 若是n%(k+1)==0,那么先手必败。 <s>这一切是显而易见,毫无疑问的</s>ide

一道例题 Tang and Jiang are good friends. To decide whose treat it is for dinner, they are playing a game. Specifically, Tang and Jiang will alternatively write numbers (integers) on a white board. Tang writes first, then Jiang, then again Tang, etc... Moreover, assuming that the number written in the previous round is X, the next person who plays should write a number Y such that 1 <= Y - X <= k. The person who writes a number no smaller than N first will lose the game. Note that in the first round, Tang can write a number only within range [1, k] (both inclusive). You can assume that Tang and Jiang will always be playing optimally, as they are both very smart students.函数

这一道题目大意上和Bush Game 差很少,可是不一样的是它里面说的是写不出不小于n的就输了,那么必胜的时候就是你已经写出了n-1的时候。因此此时若是(n-1)%(k+1)==0那么先手一定输,不然就是后手输,由于先手那一次后可已转换为第一种状况spa

尼姆博弈 (Nim Game)

有n堆物体,每一堆的数量为a[i]个,每一次一我的任选一堆取出任意个(不能为0) 设k为每一堆异或的结果,若是k==0,那么先手一定失败,不然先手一定胜利。 这样来想,把每一堆的数量转换成二进制而后竖着来最低位对齐。若是说k==0,那么这意味这每个列的1的个数必定会是偶数个。而第一我的取走了一些之后呢,这意味着必定一些列的1的个数为变成奇数个,那么k必定就再也不等于0了。而此时后手的人只要把异或的值修正为0就能够了。 由于<font color=red>k不等于0了,那么这就说明k的最高位必定会是1,而这个最高位的1必定会是a[i]中的对应位上的1提供的</font>。 而根据异或的性质,k和a[i]异或结果为其余的异或结果。 咱们能够在a[i]中减去一些值,是a[i]最终等于k和a[i]异或的结果,<font color=red>又由于k最高位的1是由a[i]提供的,那么那里必定会变成0,就必定会比a[i]小,而只要把这里减掉,k就又会等于0了</font>code

SG函数

公平组合游戏

1.双方交替来进行; 2.游戏进行的任意时刻,能够执行的合法行动与哪个玩家执行无关; 3.当玩家没法行动的时候,就判负排序

mex运算

$$ mex(S)=min{x|x\in N,x\notin S} $$队列

SG函数

对于任意状态下的x $$ SG(x)=mex{SG(y)|y是x的后继状态} $$ 对于终止状态,SG值为0。游戏

若是某一状态后继SG有0,则当前状态为N 若是当前状态全部后继SG不为0,则当前为P;ip

好的,既然已经知道了SG函数是什么,那么就有一道题了。ci

移棋子游戏

<font color=blue>Description</font>

给定一个有N个节点的DAG图,图上某些节点上面有棋子。两名玩家交替移动棋子,玩家每一次能够将任意一颗棋子沿着有向边移动到下一个节点,当没法移动的时候,就输掉了游戏,假设双方都足够聪明,问先手必胜仍是后手必胜。

<font color=blue>Input</font>

第一行三个整数N,M,K,表示N个节点M条边K个棋子。接下来M行,每行两个整数x,y,表明x节点到y节点的有向边再接下来K行,表示K个棋子所在的节点编号。

<font color=blue>output</font>

先手胜输出"win",不然输出"lose"

<font color=blue>Sample Input</font>

6 8 4
2 1
2 4
1 4
1 5
4 5
1 3
3 5
3 6
1 2 4 6

<font color=blue>Sample Output</font>

win

这应该是我第一次这么抄题吧。 DAG图,看到这个应该回想起拓扑排序,其次,由于没有后继的点SG值为0,而每个点的SG值又是有它的后继决定的。若是它的后继没有弄完,那么就不能够算。这很像拓扑排序把入度为0的点push进队列。 所以不难想到这一题能够用相似于拓扑排序的方法来作。咱们须要存两个图,正向的和反向的。正向的图用来寻找这一个点的后继获取这一个点的SG值,反向的用来找祖宗,修改祖宗的出度,并把出度为0的祖宗push进入队列 由于有拓扑排序的基础,因此这一道题能够比较容易的作出来了

#include<bits/stdc++.h>
#define maxn 2003
using namespace std;
queue<int>Q;
vector<int>G[maxn];//正着存图
vector<int>G_[maxn];//反着存图
int chess[maxn],SG[maxn];//记录棋子的位置 ,SG函数值 
bool vis[maxn*3];
int n,m,outdgr[maxn];//记录点,边,出度 
int k; 

void solve(){
	for(int i=1;i<=n;i++){
		if(!outdgr[i]){
			Q.push(i);
		}
	}
	
	while(!Q.empty()){	
		memset(vis,0,sizeof(vis));	
		int u=Q.front();Q.pop();
		vector<int>::iterator iter=G[u].begin();
		int maxsg=0;
		while(iter!=G[u].end()){
			maxsg=max(maxsg,SG[*iter]);
			vis[SG[*iter]]=true;
			iter++;
		}
		int j;
		for(j=0;j<=maxsg+1;j++)if(!vis[j])break;
		SG[u]=j;//获得这一个点的SG值 
		vector<int>::iterator iter_=G_[u].begin();
		while(iter_!=G_[u].end()){
			outdgr[*iter_]--;
			if(!outdgr[*iter_])Q.push(*iter_);
			iter_++;
		}
	}
	int ans=SG[chess[1]];
	for(int i=2;i<=k;i++)ans^=SG[chess[i]];
	if(!ans)printf("lose\n");
	else printf("win\n");
	return ; 
}

int main(){
	memset(outdgr,0,sizeof(outdgr));
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		G[u].push_back(v);
		G_[v].push_back(u); 
		outdgr[u]++;
	}
	for(int i=1;i<=k;i++){
		scanf("%d",&chess[i]);
	}
	solve();
	return 0;
}

那么,That's all.

相关文章
相关标签/搜索