hdu 4317 Unfair Nim(数位DP,4级)

Unfair Nim

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 662    Accepted Submission(s): 271


Problem Description
Alice and Bob are tired of playing the Nim game, because they are so clever that before the beginning of the game, they have already known the result. Here is their conversation:

Bob: It's unfair. I am always the second player, but you know, if the number of stones in each pile are distributed uniformly, at most time the first player, i.e., you, will win.
Alice: Yes, I agree with you. So I give you a chance to beat me, you are allowed to add some stones in some piles (but you can't create a new pile) before the game starts, so that you can win as the second player.
Bob: Yeah, that's cool. I will win definitely.
Alice: But note, you must add the minimum of the stones. If you add more stones than necessary to win, your winning will be cancelled.
Bob: er... Let me see...

For the readers who are not familiar with the Nim game (from Wikipedia):
Nim is a mathematical game of strategy in which two players take turns removing stones from distinct heaps. On each turn, a player must remove at least one stone, and may remove any number of stones provided they all come from the same heap. The player who take the last stone wins.
 

Input
The first line of each test case contains an integer N (1 <= N <= 10), the number of piles at the beginning. The next line contains N positive integers, indicating the number of stones in each pile. The number of stones in each pile is no more than 1,000,000.
 

Output
Output one line for each test case, indicating the minimum number of stones to add. If it is impossible, just output "impossible".
 

Sample Input
3 1 2 3 3 1 1 1 1 10
 

Sample Output
0 3 impossible
 

Author
TJU
 

Source
 

Recommend
zhuyuanchen520

思路:dp[x][y],从右向左数,第x位1是偶数状态下的进位状态y.然后就是判断各种条件了。


我们的DP就是从右边到左边来做的,首先保存每一列的状态,也是用二进制来表示的,如  7,5,0,0(假设右边是低位)

那么我们设 f[ i ][ j ]表示右边起第 i 位 保证这一列的 1 的个数是偶数的情况下,进位的状态为 j (进位的状态与上面的每列状态一样,就是每个数产生进位的状态)

那么有 f[ i ][ j ]=min{ f[ i-1][ k ]+满足进位后这一列的1 的个数为偶数,并且进位状态为1 需要加的数的和}

具体的一堆判断用位运算来做,

     tmp=s[ i ]&k表示第i列一定产生的进位   

     s[ i ]^k 表示加上之前的进位后这一列的状态 

     j^tmp表示需要再进位的是哪几位

     s[ i ]^k  &   j^tmp ==    j^tmp 只有满足需要进的位都为1 才能成功进位(如果为0 ,加1 后还不能进位,加2 的情况可以转换成高一位的计算)

     s[ i ]^k  ^   j^tmp 表示满足进位后的状态,这个状态必须满足 1 的个数为偶数,如果为 奇数并且个数少于n ,则再加随便再0的位置加个1就行,不过答案要多加一次

摘自fp_hzq


注意:凡是带&等操作的,都需要加括号,(就这,调了进一个钟头,饭都没吃。。。。)


#include<iostream>
#include<cstring>
#include<cstdio>
#define gb(x) (1<<x)
using namespace std;
const int mm=1<<11;
int dp[25][mm],s[25],bit[mm],f[25];
int n;
int getbit(int x)
{
    int ret=0;
    while(x)
    {
        ret+=x&1;
        x>>=1;
    }
    return ret;
}
int main()
{
    for(int i=0; i<mm; ++i)
        bit[i]=getbit(i);
    while(~scanf("%d",&n))
    {
        for(int i=0; i<n; ++i)
            scanf("%d",&f[i]);
        if(n<2)
        {
            puts("impossible");
            continue;
        }
        int m;
        memset(s,0,sizeof(s));
        for(int i=1; i<25; ++i)
        {
            for(int j=0; j<n; ++j)
                if(f[j]&gb(i-1))
                    s[i]|=gb(j);
            if(s[i])m=i+1;
        }
        memset(dp,0x3f,sizeof(dp));
        int ans=dp[0][0];
        dp[0][0]=0;
        int tmp;
        for(int i=1; i<=m; ++i)
            for(int j=0; j<gb(n); ++j)
                if(dp[i-1][j]<ans)
                {
                    tmp=j&s[i];///已产生的进位
                    for(int k=tmp; k<gb(n); ++k) ///k 进位状态
                    {
                        if(((k&tmp)==tmp)&&///包含已产生的
                                ((s[i]^j)&(tmp^k))==(tmp^k)&&///和需要包括进位
                                ((bit[s[i]^j^tmp^k]&1)==0||bit[s[i]^j^tmp^k]<n)/// 需要加上 tmp^k
                          )
                        {
                            ///if(k==0)puts("www");
                            int z=bit[tmp^k]+(bit[s[i]^j^tmp^k]&1);
                            dp[i][k]=std::min(dp[i][k],dp[i-1][j]+(bit[tmp^k]+(bit[s[i]^j^tmp^k]&1))*gb(i-1));
                            ///cout<<dp[i][k]<<" i k "<<i<<" "<<k<<" "<<j<<" "<<z<<" "<<tmp<<endl;
                        }
                    }
                }
        for(int i=0; i<gb(n); ++i)
            if((bit[i]&1)==0)
            {
                ans=std::min(ans,dp[m][i]);
            }
        printf("%d\n",ans);
    }
}




posted @ 2013-06-05 13:19  剑不飞  阅读(271)  评论(0编辑  收藏  举报