【TC SRM701】PartisanGame(博弈+循环节?)
好不容易上了个DIV1,比赛时间这么好,想想就打打呗……一打又掉下去了。。。进重现,测了一下,挂了一组=.=
循环节不一定从0开始…………GG。。。
新鲜的TC题也不好找题解=。=不知道这种做法对不对……
题目大意:
喜闻乐见的Alice和Bob玩石头
不过规则稍微变了下。
n块石头
(
n
≤
1
0
9
)
(n \le 10^9)
(n≤109)
Alice先手,轮流取,给出两个数组,表示Alice和Bob每次能取的石头数,保证在1~5颗内。即Alice和Bob每次只能取各自数组内某一个数那么多的石头。无法操作为输
问谁赢。
每次取的石头都在15,可以考虑暴力找SG函数。对于i个石头的情况,可以由i-5i-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 PartisanGame
using 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;