首页 > 代码库 > HDU 4778 Gems Fight! (状态压缩 + 博弈)

HDU 4778 Gems Fight! (状态压缩 + 博弈)

OJ题目 : click here ~~

题目分析:G种颜色的宝石(Gems),放在B个包里,每个包里同种宝石的个数不定哦。A,B轮流取一个包,宝石放在cooker上,同种颜色的宝石达到S个时,就可以融合成一个魔法石,如果2*S个当然就可以融合成两个魔法石啦。某人在某轮获得了魔法石,可以接着再玩一轮,直到没有获得魔法石。A先开始。问A比B 最多能多多少个魔法石。A,B都采用最优策略。

B的值最大为21,可以二进制表示每一个状态。

设dp[ i ]  表示状态 i 时,A比B最多多dp [ i ] 个魔法石。  详见代码注释。

AC_CODE

int G , B , S ;
int Back[25][11] ;
int dp[1<<21] ;//状态为i时,先手比后手多的魔法石的个数。i的二进制表示中1标示未选过,0标示已经选过。
int remain[11];
int have[11];
int main(){
    while(scanf("%d%d%d",&G , &B , &S) != EOF){
        if(G == 0 && B == 0 && S == 0) break;
        memset(Back , 0 , sizeof(Back));
        int i , j , k , x;
        for(i = 0;i < B;i++){
            scanf("%d",&k);
            while(k--){
                scanf("%d",&x);
                Back[i][x]++;
            }
        }
        dp[0] = 0;
        for(i = 1;i < (1<<B);i++){//计算到i状态,cooker上各种颜色宝石的个数。
            memset(remain , 0 , sizeof(remain));
            dp[i] = -(1<<30);
            for(j = 0;j < B;j++){
                if((i & (1<<j)) == 0){
                    for(k = 1;k <= G;k++){
                        remain[k] += Back[j][k];
                        while(remain[k] >= S) remain[k] -= S;
                    }
                }
            }
            for(j = 1;j <= G;j++)
                have[j] = remain[j];
            for(j = 0;j < B;j++){//计算状态i下,选择包j

                if(i & (1<<j)){//计算选择包j时,能获得多少魔法石
                    int bonus = 0;
                    for(k = 1;k <= G;k++){
                        int a = have[k] + Back[j][k];
                        while(a >= S){
                            bonus++;
                            a -= S;
                        }
                    }

                    if(bonus > 0) dp[i] = max(dp[i] , bonus + dp[i^(1<<j)]);//如果能获得魔法,下轮依然是本人
                        else dp[i] = max(dp[i] , - dp[i^(1<<j)]);//没有获得魔法石,下轮是对手
                }
            }
        }
        cout << dp[(1<<B) - 1] << endl;
    }
    return 0 ;
}