1. 程式人生 > >“浪潮杯”第九屆山東省ACM大學生程式設計競賽重現賽 G game (尼姆博弈)

“浪潮杯”第九屆山東省ACM大學生程式設計競賽重現賽 G game (尼姆博弈)

題目描述

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;
}