【TC SRM701】PartisanGame(博弈+迴圈節。)
好不容易上了個DIV1,比賽時間這麼好,想想就打打唄……一打又掉下去了。。。進重現,測了一下,掛了一組=.=
迴圈節不一定從0開始…………GG。。。
新鮮的TC題也不好找題解=。=不知道這種做法對不對……
題目大意:
喜聞樂見的Alice和Bob玩石頭
不過規則稍微變了下。
n塊石頭 (n≤109) (n \le 10^9)
Alice先手,輪流取,給出兩個數組,表示Alice和Bob每次能取的石頭數,保證在1~5顆內。即Alice和Bob每次只能取各自數組內某一個數那麼多的石頭。無法操作為輸
問誰贏。
每次取的石頭都在1~5,可以考慮暴力找SG函數。對於i個石頭的情況,可以由i-5~i-1轉移來。
但n很大,打個小表可以發現是有迴圈的。
但不一定從起點開始。
然後我就各種特判……其實較大後一定會出現迴圈,比如1000後一定是迴圈……但。。。。。唉……掛在了一組上……GG
然後最後就是暴力DP前2000,n < 1000的話直接輸出,否則找迴圈,。
代碼如下:
#include <iostream>#include <cmath>#include <vector>#include <cstdlib>#include <cstdio>#include <cstring>#include <queue>#include <stack>#include <list>#include <algorithm>#include <map>#include <set>#include <vector>#define LL long long#define Pr pair<int,int>#define fread(ch) freopen(ch,"r",stdin)#define fwrite(ch) freopen(ch,"w",stdout)#define classname PartisanGameusing namespace std;const int INF = 0x3f3f3f3f;const int msz = 10000;const double eps = 1e-8;class classname{public: int dp[2][2333],c[2]; bool vis[10]; string getWinner(int n, vector <int> a, vector <int> b) { dp[0][0] = 0; dp[1][0] = 0; int en = (n >= 1000? 1000: n); //int en = 1000; memset(c,0,sizeof(c)); c[0]++; for(int i = 1; i <= en; ++i) { int id = (i <= 5? i: 5); int tp = i; memset(vis,0,sizeof(vis)); for(int j = 0; j < a.size(); ++j) { if(id-a[j] < 0) continue; vis[dp[0][tp-a[j]]] = 1; } for(int j = 0; ; ++j) { if(!vis[j]) { dp[1][tp] = j; break; } } memset(vis,0,sizeof(vis)); for(int j = 0; j < b.size(); ++j) { if(id-b[j] < 0) continue; vis[dp[1][tp-b[j]]] = 1; } for(int j = 0; ; ++j) { if(!vis[j]) { dp[0][tp] = j; break; } } c[dp[1][i]? 1: 0]++; } if(n <= 1000) return dp[1][n]? "Alice": "Bob"; else { if(c[1] <= 30) return "Bob"; if(c[0] <= 30) return "Alice"; int rk = 5; int st = 0; while(1) { rk = 5; for(; rk < 30; ++rk) { bool f = 1; for(int i = 0; i < rk; ++i) { if(dp[1][st+i] != dp[1][st+i+rk]) { f = 0; break; } } if(f) break; } if(rk < 30) break; st++; } //printf("%d ",rk); return dp[1][(n-st)%rk+st]? "Alice": "Bob"; } }} test;