程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 博弈問題之SG函數博弈小結

博弈問題之SG函數博弈小結

編輯:C++入門知識

SG函數: 給定一個有向無環圖和一個起始頂點上的一枚棋子,兩名選手交替的將這枚棋子沿有向邊進行移動,無法移 動者判負。事實上,這個游戲可以認為是所有Impartial Combinatorial Games的抽象模型。也就是說,任何一個ICG都可以通過把每個局面看成一個頂點,對每個局面和它的子局面連一條有向邊來抽象成這個“有向圖游戲”。下 面我們就在有向無環圖的頂點上定義Sprague-Garundy函數。首先定義mex(minimal excludant)運算,這是施加於一個集合的運算,表示最小的不屬於這個集合的非負整數。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。 在我的理解中,sg函數就是一個對有向無環圖dfs的過程,在處理nim博弈時,多個石堆可以看成多個sg函數值的異或。 例題: POJ2311 Cutting Game 典型的sg博弈,找後繼狀態。題意是給出一個n*m的紙片,每次剪成兩部分,誰先剪到1*1就勝利。這就是一個找後繼的題目,每次剪成的兩部分就是前一狀態的後繼,只要將兩個部分的sg值異或起來就是前一狀態的sg值。 [cpp]   #include<iostream>   #include<cstdio>   #include<algorithm>   #include<cstring>   #include<string>   #include<cmath>   #include<set>   #include<vector>   #include<stack>   #define mem(a,b) memset(a,b,sizeof(a))   #define FOR(a,b,i) for(i=a;i<=b;++i)   #define For(a,b,i) for(i=a;i<b;++i)   #define N 1000000007   using namespace std;   inline void RD(int &ret)   {       char c;       do       {           c=getchar();       }       while(c<'0'||c>'9');       ret=c-'0';       while((c=getchar())>='0'&&c<='9')       {           ret=ret*10+(c-'0');       }   }   inline void OT(int a)   {       if(a>=10)       {           OT(a/10);       }       putchar(a%10+'0');   }   int sg[201][201];   int dfs(int a,int b)//sg函數的一般寫法   {       if(sg[a][b]>=0)       {           return sg[a][b];       }       int i,map[201],r;//map標記數組一定要在dfs內部定義,不然會出現錯誤       mem(map,0);       FOR(2,(a/2),i)       {           r=dfs(i,b)^dfs(a-i,b);//後繼的異或得到前一狀態的sg值           map[r]=1;       }       FOR(2,(b/2),i)       {           r=dfs(a,i)^dfs(a,b-i);           map[r]=1;       }       FOR(0,200,i)       {           if(map[i]==0)           {               return sg[a][b]=i;//mex公式的應用           }       }   }   int main()   {       int n,m,sum;       mem(sg,-1);       while(scanf("%d%d",&n,&m)!=EOF)       {           sum=dfs(n,m);           if(sum>0)           {               printf("WIN\n");           }           else           {               printf("LOSE\n");           }       }       return 0;   }     POJ2425 A Chess Game 題意是給你一個拓撲圖,一個起點上的n個棋子,兩個玩家交替移動棋子,誰無法移動誰輸,典型的sg函數運用。套用模板就行了。此題數據量較大,加入了輸入優化後刷到了第一版第四名,nice! [cpp]   #include<iostream>   #include<cstdio>   #include<algorithm>   #include<cstring>   #include<string>   #include<cmath>   #include<set>   #include<vector>   #include<stack>   #define mem(a,b) memset(a,b,sizeof(a))   #define FOR(a,b,i) for(i=a;i<=b;++i)   #define For(a,b,i) for(i=a;i<b;++i)   #define N 1000000007   using namespace std;   inline void RD(int &ret)   {       char c;       do       {           c=getchar();       }       while(c<'0'||c>'9');       ret=c-'0';       while((c=getchar())>='0'&&c<='9')       {           ret=ret*10+(c-'0');       }   }   inline void OT(int a)   {       if(a>=10)       {           OT(a/10);       }       putchar(a%10+'0');   }   int vis[1001][1001],sg[1001];   int n;   int dfs(int x)//典型的sg過程   {       int i;       if(sg[x]!=-1)       {           return sg[x];       }       int f[1001];       mem(f,0);       For(0,n,i)       {           if(vis[x][i]!=-1)           {               f[dfs(i)]=1;           }       }       i=0;       while(f[i])       {           i++;       }       return sg[x]=i;   }   int main()   {       int i,j,k,t,x,p,sum;       while(scanf("%d",&n)!=EOF)       {           mem(vis,-1);           mem(sg,-1);           For(0,n,i)           {               RD(k);               if(k==0)               {                   sg[i]=0;               }               For(0,k,j)               {                   RD(t);                   vis[i][t]=1;//建圖               }           }           while(1)           {               RD(x);               if(x==0)               {                   break;               }               sum=0;               For(0,x,i)               {                   RD(p);                   sum^=dfs(p);               }               if(sum!=0)               {                   printf("WIN\n");               }               else               {                   printf("LOSE\n");               }           }       }       return 0;   }     POJ2068 Nim 題意是圓桌上有2n個人,奇數一隊,偶數一隊,每個人都有一個拿走棋子的最高限額,問你最後1對能否獲勝。 還是用強大的sg函數過的,記錄下每個狀態的sg。 [cpp]   #include<iostream>   #include<cstdio>   #include<algorithm>   #include<cstring>   #include<string>   #include<cmath>   #include<set>   #include<vector>   #include<stack>   #include <queue>   #include<map>   #define mem(a,b) memset(a,b,sizeof(a))   #define FOR(a,b,i) for(i=a;i<=b;++i)   #define For(a,b,i) for(i=a;i<b;++i)   using namespace std;   inline void RD(int &ret)   {       char c;       do       {           c=getchar();       }       while(c<'0'||c>'9');       ret=c-'0';       while((c=getchar())>='0'&&c<='9')       {           ret=ret*10+(c-'0');       }   }   inline void OT(int a)   {       if(a>=10)       {           OT(a/10);       }       putchar(a%10+'0');   }   int n,s,m[22],sg[22][8200],sum;   int dfs(int x,int y)   {       if(sg[x][y]!=-1)       {           return sg[x][y];       }       int i,j;       FOR(1,m[x],i)       {           if(y-i<0)           {               break;           }           if(x+1>=2*n)           {               j=0;           }           else           {               j=x+1;           }           if(dfs(j,y-i)==0)           {               return sg[x][y]=1;           }       }       return sg[x][y]=0;   }   int main()   {       int i;       while(1)       {           RD(n);           if(n==0)           {               break;           }           RD(s);           For(0,2*n,i)           {               RD(m[i]);           }           mem(sg,-1);           FOR(0,2*n,i)           {               sg[i][0]=1;           }           sum=dfs(0,s);           if(sum==0)           {               printf("0\n");           }           else           {               printf("1\n");           }       }       return 0;   }     POJ3537 Crosses and Crosses 題意:給出一個1*n的矩形,上面有n個方格,現有兩人分別在上面畫×,誰先能畫出三個×相連就贏了。這就是一個sg函數的母問題轉化為子問題的題目,由於在第x位置畫了×後,則就轉變成(x-3)個格子畫×和(n-x-2)個格子畫×。。。這就能不斷的分解下去,最後將所有sg值異或起來就是正解了。 [cpp]   #include<iostream>   #include<cstdio>   #include<algorithm>   #include<cstring>   #include<string>   #include<cmath>   #include<set>   #include<vector>   #include<stack>   #include <queue>   #include<map>   #define mem(a,b) memset(a,b,sizeof(a))   #define FOR(a,b,i) for(i=a;i<=b;++i)   #define For(a,b,i) for(i=a;i<b;++i)   using namespace std;   inline void RD(int &ret)   {       char c;       do       {           c=getchar();       }       while(c<'0'||c>'9');       ret=c-'0';       while((c=getchar())>='0'&&c<='9')       {           ret=ret*10+(c-'0');       }   }   inline void OT(int a)   {       if(a>=10)       {           OT(a/10);       }       putchar(a%10+'0');   }   int sg[2001];   int dfs(int x)   {       if(x<0)       {           return 0;       }       if(sg[x]>=0)       {           return sg[x];       }       int i,y;       bool v[2001]={false};       FOR(1,x,i)       {           y=dfs(i-3)^dfs(x-i-2);//找後繼(經典)           v[y]=true;       }       for(i=0;;i++)       {           if(v[i]==false)           {               return sg[x]=i;           }       }   }   int main()   {       int n,sum;       mem(sg,-1);       while(scanf("%d",&n)!=EOF)       {           sum=dfs(n);           if(sum)           {               printf("1\n");           }           else           {               printf("2\n");           }       }       return 0;   }     POJ2599 A funny game 記憶化搜索,這題的博弈味道不濃,更多的是搜索。題意是給一個圖,兩人輪流移動,走過的節點不能再走。水題,dfs+標記就行。 [cpp]   #include<iostream>   #include<cstdio>   #include<algorithm>   #include<cstring>   #include<string>   #include<cmath>   #include<set>   #include<vector>   #include<stack>   #define mem(a,b) memset(a,b,sizeof(a))   #define FOR(a,b,i) for(i=a;i<=b;++i)   #define For(a,b,i) for(i=a;i<b;++i)   #define N 1000000007   using namespace std;   inline void RD(int &ret)   {       char c;       do       {           c=getchar();       }       while(c<'0'||c>'9');       ret=c-'0';       while((c=getchar())>='0'&&c<='9')       {           ret=ret*10+(c-'0');       }   }   inline void OT(int a)   {       if(a>=10)       {           OT(a/10);       }       putchar(a%10+'0');   }   int n,v[1001][1001],vis[1001];   int dfs(int x)   {       int i;       FOR(1,n,i)       {           vis[x]=1;           if(v[i][x]&&!vis[i])           {               if(!dfs(i))               {                   vis[x]=0;                   return i;               }           }           vis[x]=0;       }       return 0;   }   int main()   {       int m,i,a,b;       while(scanf("%d%d",&n,&m)!=EOF)       {           mem(v,0);           mem(vis,0);           FOR(1,n-1,i)           {               RD(a);               RD(b);               v[a][b]=v[b][a]=1;           }           i=dfs(m);           if(i!=0)           {               printf("First player wins flying to airport %d\n",i);           }           else           {               printf("First player loses\n");           }       }       return 0;   }      

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved