Unfair Nim
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 662 Accepted Submission(s): 271
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
思路: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);
}
}