首先先說兩個定義 N狀態:<font color=red>前面的一個玩家</font>必勝 P狀態: <font color=red>後面一個玩家</font>必勝c++
有一堆數量爲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
有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
1.雙方交替來進行; 2.遊戲進行的任意時刻,能夠執行的合法行動與哪個玩家執行無關; 3.當玩家沒法行動的時候,就判負排序
$$ mex(S)=min{x|x\in N,x\notin S} $$隊列
對於任意狀態下的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.