【Nim && 01背包 取不多于m个数异或组成数res的方案数】SDUT-4220 Games

Step1 Problem:

有n堆石子,每堆石子数量对应为a[i]。A和B两个人玩Nim游戏,A先手,B可以选择删除不多于m堆石子,使得B获胜的方案数 取模 1e9+7。
数据范围:
正整数 n<=1e3, d <= 10, a[i] <= 1e3.
例子:
Input:
2
5 2
1 1 2 3 4
6 3
1 2 4 7 1 2
Output:
2
5

Step2 Ideas:

Nim游戏: 每堆石子数量 异或和 为0,则先手必输,否则先手必胜。
假设 res = 每堆石子数量异或和;
删除石子堆异或 = res,那么就算是一种方案。
也就是求取不多于m个数异或组成数res的方案数。
01背包
状态dp[j][k]:取 j 个数组成 k 的方案数。
对于第 i 个数 状态转移方程:
dp[j][k] = dp[j][k] + dp[j-1][k^a[i]];

Step3 Code:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 1024;
const int MOD = 1e9+7;
int a[N];
ll dp[15][N*2];
int main()
{
    int T, n, d;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d %d", &n, &d);
        int sum = 0, Max = 0;
        for(int i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
            Max = max(Max, a[i]);
            sum ^= a[i];
        }
        memset(dp, 0, sizeof(dp));
        dp[0][0] = 1;
        for(int i = 0; i < n; i++)
        {
            for(int j = min(d, n); j >= 1; j--)
            {
                for(int k = 0; k <= Max*2; k++)
                    dp[j][k] = (dp[j][k] + dp[j-1][k^a[i]])%MOD;
            }
        }
        ll ans = 0;
        for(int i = 0; i <= min(d, n); i++)
            ans += dp[i][sum], ans %= MOD;
        printf("%lld\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/bbbbswbq/article/details/80543612
Nim