http://acm.hdu.edu.cn/showproblem.php?pid=4317
题意:
有N堆石子,每堆石子都有一定数目的石子,现在你可以往石堆中加任意多的石子,使得先手必败。
思路:
首先我们可以可以发现的一点就是,根据Nim博弈的知识,我们知道N堆石子先手必败的条件是N堆石子的石子数异或值为0,那么问题就转变成了在N堆石子中加一定数量的石子,使得最后异或值变成0。因为异或是一个按照每位来进行的运算,因此我们考虑逐位来进行,对于N个数的某一位,它的值取决于前一个(i-1)对其的进位和本身加上的值,因此我们在满足第i位的时候,需要考虑的一个因素是i-1的进位,我们用一个状态j来表示进行情况,这样第i位的最终结果应该是本身的值然后加上进位过来的值,在加上额外加上的值就是最终的值了。到这里我们就可以想到状态压缩dp了,用F(i,j) 表示前i位都已经变成了0 ,并且第i位向第i+1位进位的状态为j的最少需要加的石子数。状态转移当然是由i-1来,那么就还需要枚举第i-1为向第i位的进位k,然后运用位运算进行求解。
具体的实现过程是这样的:
已经产生的进位情况为:tmp = num[i] & k
第i进位之后的值为 : y = num[i] ^ k ;
判断已经产生的进位是否合法: j & tmp == tmp ?
还需要多少位的进位: x = j ^ tmp
进位需要的代价:t[ x & y ] * d[i] + t[ x^y&x ] * 2 * d[i] ;
进位完全之后的新状态为 : now = y & (~x )
代码:
#include <stdio.h> #include <string.h> const int BB = 21 ; int N ; int t[1<<BB] ; int d[BB+1] ; int num[BB] ; int val[BB] ; int maxbit ; int dp[BB][1<<10] ; void init(){ d[1] = 1 ; for(int i=2;i<=BB;i++) d[i] = d[i-1]<<1 ; for(int i=0;i<(1<<BB);i++){ int tmp = i ; t[i] = 0 ; while( tmp ) { if( tmp&1 ) t[i] ++ ; tmp >>= 1 ; } } } int MIN(int a, int b){ if(a == -1) return b ; else return a < b ? a : b ; } void solve(){ memset(dp, -1, sizeof(dp)); dp[0][0] = 0 ; int MM = 1 << N ; for(int i=1;i<=maxbit;i++){ for(int j=0;j<MM;j++){ if( dp[i-1][j] == -1 ) continue ; int tmp = num[i] & j ; int v = num[i] ^ j; for(int k=tmp;k<MM;k++){ if( (k&tmp) == tmp ) { int x = k ^ tmp ; int add = t[x & v]*d[i] + t[ (x^v)&x ] * 2 * d[i] ; int now = v & (~x) ; if( (t[now]&1)==0 ){ dp[i][k] = MIN(dp[i][k] , dp[i-1][j] + add ); } else if( t[now] != N ){ dp[i][k] = MIN( dp[i][k] , dp[i-1][j] + add + d[i] ); } } } } } int ans = -1 ; for(int j=0;j<MM;j++){ if( dp[maxbit][j]==-1 || t[j]%2==1) continue ; ans = MIN( ans ,dp[maxbit][j] ) ; } if(ans == -1) printf("impossible\n"); else printf("%d\n",ans); } int main(){ init() ; while( scanf("%d",&N) == 1){ maxbit = 0 ; for(int i=1;i<=N;i++){ scanf("%d",&val[i]); int tmp = val[i] ; int c = 0 ; while( tmp ){ c ++ ; tmp >>=1 ; } if( maxbit < c ) maxbit = c ; } for(int i=1;i<=maxbit;i++){ num[i] = 0 ; for(int j=1;j<=N;j++){ if( val[j]&d[i] ) num[i] += d[j] ; } } solve() ; } return 0 ; }