1. 程式人生 > >ACM 第十二天

ACM 第十二天

nbsp blank move 尼姆博弈 博弈 ati 斐波那契博弈 sqrt out

博弈論(巴什博奕,威佐夫博弈,尼姆博弈,斐波那契博弈,SG函數,SG定理)

一. 巴什博奕(Bash Game):

A和B一塊報數,每人每次報最少1個,最多報4個,看誰先報到30。這應該是最古老的關於巴什博奕的遊戲了吧。

其實如果知道原理,這遊戲一點運氣成分都沒有,只和先手後手有關,比如第一次報數,A報k個數,那麽B報5-k個數,那麽B報數之後問題就變為,A和B一塊報數,看誰先報到25了,進而變為20,15,10,5,當到5的時候,不管A怎麽報數,最後一個數肯定是B報的,可以看出,作為後手的B在個遊戲中是不會輸的。

那麽如果我們要報n個數,每次最少報一個,最多報m個,我們可以找到這麽一個整數k和r,使n=k*(m+1)+r,代入上面的例子我們就可以知道,如果r=0,那麽先手必敗;否則,先手必勝。

巴什博奕:只有一堆n個物品,兩個人輪流從中取物,規定每次最少取一個,最多取m個,最後取光者為勝。

 1 #include <iostream>
 2 using namespace std;
 3 int main()
 4 {
 5     int n,m;
 6     while(cin>>n>>m)
 7       if(n%(m+1)==0)  cout<<"後手必勝"<<endl;
 8       else cout<<"先手必勝"<<endl;
 9     return
0; 10 } 11

二. 威佐夫博弈(Wythoff Game):

有兩堆各若幹的物品,兩人輪流從其中一堆取至少一件物品,至多不限,或從兩堆中同時取相同件物品,規定最後取完者勝利。

直接說結論了,若兩堆物品的初始值為(x,y),且x<y,則另z=y-x;

記w=(int)[((sqrt(5)+1)/2)*z ];

若w=x,則先手必敗,否則先手必勝。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <iostream>
 4 using namespace
std; 5 int main() 6 { 7 int n1,n2,temp; 8 while(cin>>n1>>n2) 9 { 10 if(n1>n2) swap(n1,n2); 11 temp=floor((n2-n1)*(1+sqrt(5.0))/2.0); 12 if(temp==n1) cout<<"後手必勝"<<endl; 13 else cout<<"先手必勝"<<endl; 14 } 15 return 0; 16 } 17 18

三. 尼姆博弈(Nimm Game):

尼姆博弈指的是這樣一個博弈遊戲:有任意堆物品,每堆物品的個數是任意的,雙方輪流從中取物品,每一次只能從一堆物品中取部分或全部物品,最少取一件,取到最後一件物品的人獲勝。

結論就是:把每堆物品數全部異或起來,如果得到的值為0,那麽先手必敗,否則先手必勝。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <iostream>
 4 using namespace std;
 5 int main()
 6 {
 7     int n,ans,temp;
 8     while(cin>>n)
 9     {
10         temp=0;
11         for(int i=0;i<n;i++)
12         {
13             cin>>ans;
14             temp^=ans;
15         }
16         if(temp==0)  cout<<"後手必勝"<<endl;
17         else cout<<"先手必勝"<<endl;
18     }
19     return 0;
20 }
21  
22  

四. 斐波那契博弈:

有一堆物品,兩人輪流取物品,先手最少取一個,至多無上限,但不能把物品取完,之後每次取的物品數不能超過上一次取的物品數的二倍且至少為一件,取走最後一件物品的人獲勝。

結論是:先手勝當且僅當n不是斐波那契數(n為物品總數)

 1 #include <iostream>  
 2 #include <string.h>  
 3 #include <stdio.h>  
 4 using namespace std;  
 5 const int N = 55;    
 6 int f[N];   
 7 void Init()  
 8 {  
 9     f[0] = f[1] = 1;  
10     for(int i=2;i<N;i++)  
11         f[i] = f[i-1] + f[i-2];  
12 }    
13 int main()  
14 {  
15     Init();  
16     int n;  
17     while(cin>>n)  
18     {  
19         if(n == 0) break;  
20         bool flag = 0;  
21         for(int i=0;i<N;i++)  
22         {  
23             if(f[i] == n)  
24             {  
25                 flag = 1;  
26                 break;  
27             }  
28         }  
29         if(flag) puts("Second win");  
30         else     puts("First win");  
31     }  
32     return 0;  
33 } 

五、SG函數,SG定理

必勝點和必敗點的概念 P點:必敗點,換而言之,就是誰處於此位置,則在雙方操作正確的情況下必敗。 N點:必勝點,處於此情況下,雙方操作均正確的情況下必勝。 必勝點和必敗點的性質 1、所有終結點是 必敗點 P 。(我們以此為基本前提進行推理,換句話說,我們以此為假設) 2、從任何必勝點N 操作,至少有一種方式可以進入必敗點 P。 3、無論如何操作,必敗點P 都只能進入 必勝點 N。

Sprague-Grundy定理(SG定理):

遊戲和的SG函數等於各個遊戲SG函數的Nim和。這樣就可以將每一個子遊戲分而治之,從而簡化了問題。而Bouton定理就是Sprague-Grundy定理在Nim遊戲中的直接應用,因為單堆的Nim遊戲 SG函數滿足 SG(x) = x。對博弈不是很清楚的請參照http://www.cnblogs.com/ECJTUACM-873284962/p/6398385.html進行進一步理解。

SG函數:

首先定義mex(minimal excludant)運算,這是施加於一個集合的運算,表示最小的不屬於這個集合的非負整數。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

對於任意狀態 x , 定義 SG(x) = mex(S),其中 S 是 x 後繼狀態的SG函數值的集合。如 x 有三個後繼狀態分別為 SG(a),SG(b),SG(c),那麽SG(x) = mex{SG(a),SG(b),SG(c)}。 這樣 集合S 的終態必然是空集,所以SG函數的終態為 SG(x) = 0,當且僅當 x 為必敗點P時。

SG(x)=x

E - Chess

HDU - 5724 Alice and Bob are playing a special chess game on an n × 20 chessboard. There are several chesses on the chessboard. They can move one chess in one turn. If there are no other chesses on the right adjacent block of the moved chess, move the chess to its right adjacent block. Otherwise, skip over these chesses and move to the right adjacent block of them. Two chesses can’t be placed at one block and no chess can be placed out of the chessboard. When someone can’t move any chess during his/her turn, he/she will lose the game. Alice always take the first turn. Both Alice and Bob will play the game with the best strategy. Alice wants to know if she can win the game.
InputMultiple test cases.

The first line contains an integer T(T100), indicates the number of test cases.

For each test case, the first line contains a single integer n(n1000), the number of lines of chessboard.

Then n lines, the first integer of ith line is m(m20), indicates the number of chesses on the ith line of the chessboard. Then m integers pj(1pj20) followed, the position of each chess.
OutputFor each test case, output one line of “YES” if Alice can win the game, “NO” otherwise.Sample Input
2
1
2 19 20
2
1 19
1 18
Sample Output
NO
YES

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int n,m,sg[1<<21],vis[21];
 4 int dfs(int x)
 5 {
 6      memset(vis,0,sizeof(vis));
 7      for(int i = 20;i>=0;i--)
 8      {
 9          if(x & (1<<i))
10          {
11              int temp = x;
12              for(int j = i-1;j>=0;j--)
13              {
14                  if(!(x&(1<<j)))
15                  {
16                      temp ^= (1<<j)^(1<<i);
17                      vis[sg[temp]]=1;
18                      break;
19                  }
20              }
21          }
22      }
23      for(int i = 0;i<=20;i++)
24          if(!vis[i])
25              return i;
26      return 0;
27 }
28 int main()
29 {
30      memset(sg,0,sizeof(sg));
31      for(int i = 0;i<(1<<20);i++)
32          sg[i]=dfs(i);
33      int T;
34      scanf("%d",&T);
35      while(T--)
36      {
37          int n;
38          scanf("%d",&n);
39          int ans = 0;
40          for(int i = 0;i<n;i++)
41          {
42              int res = 0,temp;
43              int q;
44              scanf("%d",&q);
45              while(q--)
46                 scanf("%d",&temp),res|=1<<(20-temp);
47              ans^=sg[res];
48          }
49          if(ans)
50              printf("YES\n");
51          else
52              printf("NO\n");
53      }
54  
55 }

參見原博客:https://blog.csdn.net/ac_gibson/article/details/41624623,https://www.cnblogs.com/ECJTUACM-873284962/p/6921829.html

ACM 第十二天