HDU 1847Good Luck in CET-4 Everybody!
n個石子取2的次冪個,0為terminal position
P/N分析和求SG值方法都可以,找規律的話模3餘0也能過
主要是為了練習SG的求法
#include <cstdio>#include <cstring>//單純博弈型 也可用P/N分析法int x[12],SG[1050];bool vis[1050];void init(){ for (int i=0 ; i<11 ; ++i) x[i]=1<<i; SG[0]=0; for (int i=1 ; i<1002 ; ++i)//get Sprague-Grundy value; { memset (vis , 0 , sizeof(vis)); for (int j=0 ; x[j]<=i ; ++j) { vis[SG[i-x[j]]]=true; } for (int j=0 ; ; ++j) { if(!vis[j]){SG[i]=j;break;} } } for (int i=0 ; i<100 ; ++i) printf("num=%d sg=%d\n",i,SG[i]);}int main (){ int n; init(); while (~scanf("%d",&n)) { if(SG[n])puts("Kiki"); else puts("Cici"); } return 0;}
HDU 3980 Paint Chain
n元環,每次取連續的m個元素,最先不能取者敗。
可以考慮n<m和n>=m兩種情況,第一種情況結果顯然,第二種將取過1次後剩下的n-m元鏈進行求SG值,前m-1個狀態的SG值明顯為0,m為1,之後的狀態取走m元素之後,會將剩下的2段分成2份(考慮0的情況),分別求其SG值,從而得到當前的狀態的一個後繼SG值。
#include <cstdio>#include <cstring>const int maxn=1005;int SG[maxn];bool vis[maxn];bool sg(int n , int m)//get SG value{ memset(SG , 0 , sizeof(SG)); SG[m]=1; for (int i=m+1 ; i<=n ; ++i) { memset (vis , 0 , sizeof(vis)); for (int j=0 ; j<i-m ; ++j) { vis[SG[j]^SG[i-m-j]]=true; } for (int j=0 ; ; ++j) { if(!vis[j]){SG[i]=j;break;} } } //for (int i=0 ; i<=n ; ++i) //printf("%d %d \n",i,SG[i]); return SG[n];}int main (){ int cas; int n,m; scanf("%d",&cas); for (int I=1 ; I<=cas ; ++I) { scanf("%d%d",&n,&m); if(m>n)printf("Case #%d: abcdxyzk\n",I); else if(sg(n-m,m))printf("Case #%d: abcdxyzk\n",I); else printf("Case #%d: aekdycoin\n",I); } return 0;}
POJ 3537 Crosses and Crosses
給一個1*n的長方形格子,在格子裡輪流畫X,最先得到3個連續X的人即為winner
#include <cstdio>#include <cstring>const int maxn=2005;int SG[maxn];bool vis[maxn];void sg(){ memset (SG , 0 , sizeof(SG)); SG[1]=1;SG[2]=1;SG[3]=1;//SG[4]=1; for (int i=4; i<maxn ; ++i) { memset (vis , 0 , sizeof(vis)); for (int j=2 ; j<i ; ++j) { if((i-j-1<2))vis[SG[j-2]^0]=true; else vis[SG[j-2]^SG[i-j-1-2]]=true; } for (int j=0 ; ; ++j) if(!vis[j]){SG[i]=j ; break;} } //for (int i=0; i<50 ; ++i) //printf("SG%d=%d\n",i,SG[i]);}int main (){ sg(); int n; while (~scanf("%d",&n)) { if(SG[n])puts("1"); else puts("2"); } return 0;}
POJ 2425 A Chess Game在DAG上搜尋的NIM遊戲,其實和在多堆石子裡拿東西是一樣的,要熟練各種遊戲模型之間的轉化,找出更新SG的方法就可以了一開始清空vis和visit的時候 把visit的大小寫成了sizeof(vis) DT了一天才發現Bug ><
#include <cstdio>#include <cstring>const int maxm=2005000;const int maxn=1005;struct Edge { int v,next;}edge[maxm];int head[maxn],cnt;void addedge(int u,int v){ edge[cnt].v=v; edge[cnt].next=head[u]; head[u]=cnt++;}bool vis[maxn],visit[maxn][maxn];//for dfs for sg;int SG[maxn];void dfs (int u){ if(~SG[u]) return ; //vis[u]=true; int p=head[u]; for ( ; ~p ; p=edge[p].next) { int v=edge[p].v; //printf("%d ",u); //if(vis[v])continue; dfs(v); visit[u][SG[v]]=true; } for (int i=0 ; true ; ++i) { if(!visit[u][i]){SG[u]=i;break;} }}int main (){ int n,node,v,m,t; while (~scanf("%d",&n)) { memset (head , -1 , sizeof(head)); memset (SG , -1 , sizeof(SG)); cnt=0; for (int u=0 ; u<n ; ++u) { scanf("%d",&node); if(!node)SG[u]=0; for (int i=0 ; i<node ; ++i) { scanf("%d",&v); addedge(u , v); } } memset (vis , false , sizeof(vis)); memset (visit , false , sizeof(visit)); for(int i=0 ; i<n ; ++i) { if(vis[i])continue; dfs(i); } /*for (int i=0 ; i<n ; ++i) { printf("%d\t",SG[i]); }*/ while (scanf("%d",&m),m) { int nim=0; for(int i=0 ; i<m ; ++i) { scanf("%d",&t); nim^=SG[t]; } puts(nim?"WIN":"LOSE"); } } return 0;}POJ 1704 Georgia and Bob(階梯博弈(目標的NP態只和奇數階的NP態有關)) 知道如何轉換的話就很簡單,關鍵是我不知道,推了半天,蒙了各種感覺有道理的轉化方法都WA了,最後還是看了題解,還是很好理解的。我的理解:對於棋盤上棋子,我們兩兩對其綁定,如果是奇數個,我們可以看做是和0處的一個棋子綁定,通過找規律我們可以發現,每對棋子的前一個棋子移動之後,後一個棋子可以移動和前一個棋子相同的步數是不影響結果的,即這是一個相對於比賽結果的平衡態,所以我們可以把所有的棋子對都平移到左邊並保持棋子對之間的距離不變,這時我們發現我們要做得只是讓每對棋子的後者和前者距離變為1,以及向前平移每對棋子,這兩種操作後者是不影響結果的,於是我們將前者的操作轉化成相應數值的NIM型博弈,剩下的大家就都能知道了
#include <cstdio>#include <algorithm>using namespace std;const int maxn=1005;int nim[maxn];int n;int main (){ int cas; scanf("%d",&cas); while (cas--) { scanf("%d",&n); int ans=0; nim[0]=0; for (int i=1 ; i<=n ; ++i) { scanf("%d",nim+i); } sort(nim,nim+n+1); for (int i=0 ; i<(n+1)/2 ; ++i) { ans^=(nim[n-2*i]-nim[n-2*i-1]-1); //printf("%d %d %d\n",nim[n-2*i],nim[n-2*i-1],ans); } puts(ans?"Georgia will win":"Bob will win"); } return 0;}
JOJ 2520 A special queen
#include <cstdio>#include <cstring>int SG[105][105];bool vis[5000];/*棋盤上的2維SG遞推,非組合遊戲可以不用SG*//*輸出格式很噁心*/int main (){ int l,w; SG[0][0]=0; for (int i=1 ; i<105 ; ++i) { SG[i][0]=i; for (int j=1 ; j<105 ; ++j) { SG[0][j]=j; if(i==j){SG[i][i]=i+1;continue;} memset (vis , 0 , sizeof(vis)); for (int k=0 ; k<j ; ++k) vis[SG[i][k]]=true; for (int k=0 ; k<i ; ++k) vis[SG[k][j]]=true; for (int k=1 ; k<=i&&k<=j ; ++k) vis[SG[i-k][j-k]]=true; for (int k=0 ; ; ++k) if(!vis[k]){SG[i][j]=k ; break;} } }/* for (int i=0 ; i<25 ; ++i) { for (int j=0 ; j<25 ; ++j) printf("%d ",SG[i][j]); puts(""); }*/ while (~scanf("%d*%d",&l,&w)) { if(l==1 || w==1){printf("G will win\n");continue;} bool flag=false; bool first=false; for (int i=0 ; i<l ; ++i) { if(!SG[i][w-1]) { flag=true; if(first)printf(" "); printf("(%d,%d)",i,w-1); first=true; } } for (int i=0 ; i<w-1 ; ++i) { if(!SG[l-1][i]) { flag=true; if(first)printf(" "); printf("(%d,%d)",l-1,i); first=true; } } if(!flag)printf("G will win"); printf("\n"); } return 0;}
ZOJ Monthly, September 2011
A Game Between Alice and Bob 博弈論nim的變形,合數分解,被常數給坑了
#include <cstdio>#include <cstring>#include <cmath>#define abs(a) (a)>(0)?(a):(-(a))#define min(a,b) (a>b?b:a)const int maxn=100000+123;typedef int typen;typen a[maxn];typen count[maxn];const int maxp = 5000;int prime[maxp + 1];//prime[0] is the counterint getPrime(){ memset (prime, 0, sizeof (prime)); for (int i = 2 ; i <= maxp ; i++) { if (!prime[i]) prime[++prime[0]] = i; for (int j = 1; j <= prime[0] && prime[j] * i <= maxp ; j++) { prime[prime[j]*i] = 1; if (i % prime[j] == 0) break; } } return prime[0];}int getFactors(typen x){ int facCnt = 0; typen tmp = x; for(int i = 1; prime[i]*prime[i] <= tmp ; ++i) { if(tmp % prime[i] == 0) { while(tmp % prime[i] == 0) tmp /= prime[i],++facCnt; } } if(tmp != 1) ++facCnt; return facCnt;}int cas=0;int main (){ int n; getPrime(); while ( ~scanf("%d",&n) ) { memset (count , 0 , sizeof(count)); int sum=0; for (int i=0 ; i<n ; ++i) { scanf("%d",a+i); count[i]=getFactors(a[i]); } for (int i=0 ; i<n ; ++i) { sum^=count[i]; } if(sum) { int i; for (i=0 ; i<n ; ++i)//從 { if((sum^count[i])<count[i])break; } printf("Test #%d: Alice %d\n",++cas,i+1); } else printf("Test #%d: Bob\n",++cas); } return 0;}