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 ;
}