博弈論(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.

相關文章
相關標籤/搜索