您的位置:首页 > 其它

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

2017-08-11 14:12 555 查看

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
[k]-ans)%mod+mod)%mod);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: