HDU_4317 Unfair Nim 狀態壓縮dp

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



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章