【TC SRM701】PartisanGame(博弈+循环节)

【TC SRM701】PartisanGame(博弈+循环节?)

好不容易上了个DIV1,比赛时间这么好,想想就打打呗……一打又掉下去了。。。进重现,测了一下,挂了一组=.=

循环节不一定从0开始…………GG。。。

新鲜的TC题也不好找题解=。=不知道这种做法对不对……

题目大意:
喜闻乐见的Alice和Bob玩石头
不过规则稍微变了下。
n块石头 ( n ≤ 1 0 9 ) (n \le 10^9) (n109)
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;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值