“浪潮杯”第九届山东省ACM大学生程序设计竞赛重现赛 G game (尼姆博弈)

版权声明:低调地前行,越努力越幸运! https://blog.csdn.net/SSYITwin/article/details/82498656

链接:https://www.nowcoder.com/acm/contest/123/G
来源:牛客网
 

题目描述

Alice and Bob are playing a stone game. There are n piles of stones. In each turn, a player can remove some stones from a pile (the number must be positive and not greater than the number of remaining stones in the pile). One player wins if he or she remove the last stone and all piles are empty. Alice plays first.

To make this game even more interesting, they add a new rule: Bob can choose some piles and remove entire of them before the game starts. The number of removed piles is a nonnegative integer, and not greater than a given number d. Note d can be greater than n, and in that case you can remove all of the piles.

Let ans denote the different ways of removing piles such that Bob are able to win the game if both of the players play optimally. Bob wants you to calculate the remainder of ans divided by109+7.

输入描述:

The first line contains an integer T, representing
the number of test cases.

For each test cases, the first line are two
integers n and d, which are described above.

The second line are n positive integersai,
representing the number of stones in each pile.

输出描述:

For each test case, output one integer (modulo109+7.) in a
single line, representing the number of different ways of removing piles that
Bob can ensure his victory.

示例1

输入

复制

2
5 2
1 1 2 3 4
6 3
1 2 4 7 1 2

输出

复制

2
5

1.题目含义:

       Alice和Bob在玩游戏,有n堆石头,在一些石块(数量必须是正数,并且不能大于堆中剩余石块的数量)。谁移除最后一块石头并且所有堆都空了,则他获胜(谁面临空的局面,谁将输掉本轮游戏)。Alice是先手。

    但是在游戏开始之前,Bob可以选择一些石头堆并将其全部移除,可以去除的石头堆的数量是非负整数,并且不大于给定的数字d(d>n也是可以的)。在这种情况下,Bob可以去除所有的堆,求解可以使得Bob赢得游戏的方案。

2.分析:

   可以看出,这属于尼姆博弈。尼姆博弈介绍,点击转到。由此我们可以得出结论:当亦或起来结果为0的时候,先手面临奇异局面,后手取得胜利。

  在本题中,多了附加条件:后手也就是Bob,可以在开始之前移除d堆中所有或者部分石子,以达到自己获胜的目的。移除d堆里面的石子,那么这d堆该如何选择?毋庸置疑,我们需要枚举所有情况,考虑动态规划求解。

3.求解过程:

  3.1 首先我们定义三维dp[1002][12][1030]数组,该三维数组dp[i][j][k]表示:前i堆里面移除j堆,亦或起来结果为k的方案数。

  3.2 状态转化方程:

      动态规划最重要的就是状态转化方程的推导,对于dp[i][j][k]我们可以第i堆属不属于要去除的j堆里面的一堆。故而,前i堆去j堆的异或值为k的方案数dp[i][j][k]状态有两种来源:

      3.2.1 第i堆石子属于j堆中的一堆:其方案数等于前i-1堆去掉j-1堆的异或值为k的方案数dp[i-1][j-1][k](去掉的不计异或和)

      3.2.2第i堆石子不属于j堆中的一堆;则其方案数等于前i-1堆去掉j堆的异或值为k^a[i]的方案数dp[i-1][j][k^a[i]](留下的计异或和)

4.注意:

    4.1 dp[i][j][k]数组中,k值的大小:k表示亦或的结果,它是a[i]亦或的结果,而a[i]的大小最大是不会超过1002的。1002 的二进制为:111110010,亦或起来也不会超过111111111(1023)。

   4.2数组初始化注意。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mod = 1e9+7;
int dp[1002][12][1030];//a[i]<=1002,亦或起来的值一定是小于1024(2^10) 
int a[1002];
int main()
{
    int t;
    int n,d;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &n,&d);
        for (int i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        memset(dp, 0, sizeof(dp));
        dp[1][1][0] = 1;
        dp[1][0][a[1]] = 1;
        for (int i = 2; i <= n; i++)
        {
            int num = min(i, d);
            for (int j = 0; j <= num; j++)
            {
                for (int k = 0; k < 1024; k++)
                {
                    dp[i][j][k] = (dp[i-1][j-1][k] + dp[i-1][j][k^a[i]]) % mod;//i堆里面去掉j堆,就可以有两种情况,第i堆属于j堆里面的一个,i堆不属于里面的一个 
                }
            }
        }
        int ans = 0;
        for (int i = 0; i <= min(d,n); i++)
        {
        	ans=(ans+dp[n][i][0])%mod;//只要是异或起来起来为0,那么就是Bob赢的一种情况 
        	//cout<<"i="<<i<<",dp[n][i][0]="<<dp[n][i][0]<<",ans="<<ans<<endl;
		}
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/SSYITwin/article/details/82498656