hdu 4317 Unfair Nim(状态压缩DP)——2012 Multi-University Training Contest 2

Unfair Nim

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


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
 

Source
 

Recommend
zhuyuanchen520
 

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4317

题意:给你n个数,你可以在每个数加上一个值,使得最后的n个数的异或值为0,当然要求加的数的和最小,这之前有个小小的博弈障眼法呵呵

分析:这题比赛时没有任何想法,唯一想到的就是暴搜,当然,是按地位到高位来搜索,这个可以说是这个DP题的基础,DP正是按低位到高位来做的,当然,也可以按高位到低位,不过我绝得那样太麻烦,做了这题,我再也不觉得我的位运算有多牛B了,这题其实就是位运算而已。。。。

你可以忽略上面的废话

这题有n个数,我们用 二进制表示 ,比如:

3

3 1 3

三个数的二进制表示如下:

0011

0001

0011

我们的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就行,不过答案要多加一次

好了,总体来说是道好题,我只能这样描述了,呵呵,具体看代码吧

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int mm=1<<11;
int f[25][mm],s[25],a[25],t[mm];
int i,j,k,n,m,tmp,ans;
int getbit(int x)
{
    int ret=0;
    while(x)
    {
        ret+=(x&1);
        x>>=1;
    }
    return ret;
}
int main()
{
    for(i=0;i<mm;++i)t[i]=getbit(i);
    while(~scanf("%d",&n))
    {
        for(i=0;i<n;++i)
            scanf("%d",&a[i]);
        if(n<2)
        {
            puts("impossible");
            continue;
        }
        memset(s,0,sizeof(s));
        for(i=1;i<=22;++i)
        {
            for(j=0;j<n;++j)
                if(a[j]&(1<<(i-1)))s[i]|=(1<<j);
            if(s[i])m=i+1;
        }
        memset(f,0x3f,sizeof(f));
        ans=f[0][0];
        f[0][0]=0;
        for(i=1;i<=m;++i)
            for(j=0;j<(1<<n);++j)
                if(f[i-1][j]<ans)
                {
                    tmp=j&s[i];//肯定产生进位
                    for(k=tmp;k<(1<<n);++k)
                        if((k&tmp)==tmp//满足肯定进位
                           &&((s[i]^j)&(k^tmp))==(k^tmp)//能完成进位
                           &&((t[(s[i]^j)^(k^tmp)]&1)==0||t[(s[i]^j)^(k^tmp)]<n))//进位后有偶数个1
                             f[i][k]=min(f[i][k],f[i-1][j]+(t[k^tmp]+(t[(s[i]^j)^(k^tmp)]&1))*(1<<(i-1)));
                }
        for(i=0;i<(1<<n);++i)
            if((t[i]&1)==0)
                ans=min(ans,f[m][i]);
        printf("%d\n",ans);
    }
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值