bzoj 2281: [Sdoi2011]黑白棋 && bzoj 4550: 小奇的博弈(Nimk博弈+DP)

4550: 小奇的博弈

Time Limit: 2 Sec   Memory Limit: 256 MB
Submit: 68   Solved: 42
[ Submit][ Status][ Discuss]

Description

这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色。最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色不同。
 
小奇可以移动白色棋子,提比可以移动黑色的棋子,它们每次操作可以移动1到d个棋子。每当移动某一个棋子时,这个棋子不能跨越两边的棋子,当然也不可以出界。当谁不可以操作时,谁就失败了。小奇和提比轮流操作,现在小奇先移动,有多少种初始棋子的布局会使它有必胜策略?

Input

共一行,三个数,n,k,d。对于100%的数据,有1<=d<=k<=n<=10000, k为偶数,k<=100。

Output

输出小奇胜利的方案总数。答案对1000000007取模。

Sample Input

10 4 2

Sample Output

182


这题目题面有问题,少了一句话:白棋不能往左移,黑棋不能往右移

这样的划就可以将问题转成Nimk博弈了:

有k/2堆石子,第i堆石子的个数是从左往右第i个白棋与第i个黑棋之间的距离

每个人每次可以从最多d堆石子中取任意数量的石子(相当于最多将d个白/黑棋向右/向左移动)

谁先取完谁就赢了,求是否必胜

这是经典的Nimk问题,将每堆石子数量拆成二进制,再将二进制每一位单独相加,如果所有位的和都是(d+1)的倍数则先手必败,否则先手必胜


而这题不是让你判断是否必胜,是让你判断有多少种情况

因为有n个位置,放k个棋子,所有总共有n-k个空位,也就相当于求有k/2堆石子,石子总数<=n-k的必胜局面个数

(为什么是<=,而不是=,因为最两侧的黑棋白棋不一定靠边)

这样问题就彻底转化了,先求出所有必败态,然后拿总情况数C(n, k)减去它就是答案了

dp[i][j]表示目前石子数量的二进制最高位不超过第i位,总石子数为j的必败方案数,那么有递推

dp[i+1][j+x*(d+1)*(2^i)] += dp[i][j]*C(k/2, x*(d+1))

最后答案就是C(n, k)-∑dp[maxh][i]*C(n-i-k+k/2, k/2)    (0<=i<=n-k)


#include<stdio.h>
#include<string.h>
#define mod 1000000007
#define LL long long
LL C[10005][105], dp[18][10005], er[18] = {1};
int main(void)
{
	LL i, j, n, k, d, x, ans;
	for(i=0;i<=10000;i++)
		C[i][0] = 1;
	for(i=1;i<=15;i++)
		er[i] = er[i-1]*2;
	for(i=1;i<=10000;i++)
	{
		for(j=1;j<=100;j++)
			C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
	}
	while(scanf("%lld%lld%lld", &n, &k, &d)!=EOF)
	{
		memset(dp, 0, sizeof(dp));
		dp[0][0] = 1;
		for(i=0;i<=15;i++)
		{
			for(j=0;j<=n-k;j++)
			{
				dp[i+1][j] = (dp[i+1][j]+dp[i][j])%mod;
				for(x=1;x*(d+1)*er[i]+j<=n-k&&x*(d+1)<=k/2;x++)				//总石子数不能超过n-m,并且第i位二进制为1的石子堆数不能超过总堆数
					dp[i+1][j+x*(d+1)*er[i]] = (dp[i+1][j+x*(d+1)*er[i]]+dp[i][j]*C[k/2][x*(d+1)]%mod)%mod;
			}
		}
		ans = 0;
		for(i=0;i<=n-k;i++)
			ans = (ans+dp[15][i]*C[n-i-k/2][k/2])%mod;
		printf("%lld\n", ((C[n][k]-ans)%mod+mod)%mod);
	}
	return 0;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值