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



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值