Unfair Nim
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 225 Accepted Submission(s): 83
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.
3 1 2 3 3 1 1 1 1 10
0 3 impossible
题目: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;
}