HDU 4778 Gems Fight! 2013杭州区赛I题

题意:

有不超过21个袋子,每个袋子里有不超过10个球,所有球的颜色总共不超过8种,每次alice和bob轮流拿一个袋子,将其中的球放到外面来;外面每S个同一种颜色的球会变成一块魔法石,并且带来加分,得到加分的人可以继续再拿一次,直到外面不再产生加分。问当alice先手,每个人都用最优的走法,alice的魔法石减去比bob的魔法石等于多少。


思路:

第一次敲这种剪枝和博弈,一开始想直接最大值最小和最小值最大,再加上预测剪枝,没有注意到题意给出的21个袋子其实意味着可以记忆化(1<<21)个状态,T了很多发;后来注意到1<<21可以作为记忆化,而直接记忆化用dp[i][j],表示状态i时,二进制为1的位表示已经拿过了,轮到j拿,0表示alice拿,1表示bob拿,可以使接下来alice-bob为多少分,那么答案就是dp[0][0],没有加预测,本地跑一组极限数据要20秒,但是状态写的感觉好乱,又说不上为什么;后来看了vj里别人的状压,也是最大值最小和最小值最大的思路,只要dp[i]表示状态为i时,当前轮到的这个人拿可以拿多少魔法石,i的二进制为1的位表示那个袋子没被拿过,那么答案就是dp[0]-(tot-dp[0]),tot就是总共可以产生多少块魔法石,而预测剪枝时只要dfs(st,sum),st是状态,sum是剩余多少颗魔法石,当st==0 || sum==0 return 0;这样剪一下就可以了。


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int dp[1<<21];
int bag[21][8],G,B,S;
int dfs(int st,int left,int cc[]){
    int &re = dp[st];
    if(re!=-1) return re;
    if(left==0 || st==0) return re=0;
    int maxValue=0;
    for(int i=0;i<B;i++){
        if((st>>i)&1){
            int tt[8],bonus=0,sum;
            for(int j=0;j<G;j++){
                tt[j] = bag[i][j]+cc[j];
                bonus += tt[j]/S;
                tt[j]%=S;
            }
            if(bonus)
                sum = bonus + dfs(st^(1<<i),left-bonus,tt);
            else
                sum = left - dfs(st^(1<<i),left,tt);
            if(maxValue < sum) maxValue = sum;
        }
    }
    return re = maxValue;
}
int main(){
//    freopen("data.in","r",stdin);
    while(scanf("%d%d%d",&G,&B,&S)!=EOF){
        if(G==0 && B==0 && S==0) break;
        int cc[8],sum=0;
        memset(cc,0,sizeof cc);
        memset(bag,0,sizeof bag);
        memset(dp,-1,sizeof dp);
        for(int i=0,n;i<B;i++){
            scanf("%d",&n);
            for(int j=0,ci;j<n;j++){
                scanf("%d",&ci);
                bag[i][ci-1]++;
                cc[ci-1]++;
            }
        }
        for(int i=0;i<G;i++){
            sum += cc[i]/S;
            cc[i]=0;
        }
        int ans = dfs((1<<B)-1,sum,cc);
        printf("%d\n",ans+ans-sum);
    }
    return 0;
}


你可能感兴趣的:(博弈,剪枝)