hdu 4778 状压dp

题意:

有 G 种颜色的宝石,放在 B 个袋子里(每种颜色可以放多个)。

两人轮流选袋子(每个袋子只能被选 1 次),每次将选出来的袋子中的宝石放到 cooker 中,cooker 可能会起反应。

反应条件是 cooker 中出现 S 个一样颜色的宝石,而且一旦起反应,每 S 个一样颜色的宝石就会获得 1 个魔法石(同时反应)。

作为奖励,每次反应结束后当前玩家可以再选一个袋子继续游戏。

游戏目标是自己获得的魔法石尽量多,双方都采取最优策略的情况下,问最终两个玩家的魔法石之差最大值。

思路:

数据比较小直接dp,dp[i]表示i状态下的差值的最大值。i中的1表示尚未取得箱子,0表示已经取了的箱子。枚举下一步要取哪一个箱子即可。

当取一个箱子自己仍然是先手则+dp[i^(1<<j)],否则-dp[i^(1<<j)]。当初自己在纠结选择下一步时并不能确定对于这一步的状态是否可能出现,其实根本没必要,一直枚举到初始状态的话,对于初始状态来说能够用到的状态每一步都考虑到了,而单独的每一步也都是有意义的,可以枚举下去就行,不用特意一直考虑初始状态是否能到达的每一步。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
#define PF(x) cout << "debug: " << x << " ";
#define EL cout << endl;
#define PC(x) puts(x);
typedef long long ll;
#define CLR(x, v) sizeof (x, v, sizeof(x))
using namespace std;
const int INF = 0x5f5f5f5f;
const int N = 1000050;
int g,B,s;
int dp[1<<22],c[25][25],b[15],d[15];
int main()
{

   // freopen("in.txt","r",stdin);
    //freopen("out2.txt","w",stdout);
    while(~scanf("%d%d%d",&g,&B,&s))
    {
        if(g==0&&B==0&&s==0)
            break;
        memset(c,0,sizeof(c));
        int i,j,n,t;
        for(i = 1; i <= B; i++)
        {
            scanf("%d",&n);
            while(n--)
            {
                scanf("%d",&t);
                c[i][t]++;
            }
        }
        int tot = 1<<B;
        dp[0] = 0;
        for(i = 1; i < tot; i++)
        {
            dp[i]=-1000000;
            for(int k = 1;k <= g;k++)
                b[k] = 0;
            for(j = 0; j < B; j++)
            {
                if((i&(1<<j))==0)
                {
                    for(int k = 1; k <= g; k++)
                    {
                        b[k]+=c[j+1][k];
                        while(b[k]>=s)
                            b[k]-=s;
                    }

                }
            }
            for(j = 0;j < B;j++){
                if(i&(1<<j)){

                    int cnt = 0;
                    for(int k = 1;k <= g;k++){
                        d[k] = b[k];
                        d[k] += c[j+1][k];
                        while(d[k]>=s){
                            d[k]-=s;
                            cnt++;
                        }
                    }
                   if(cnt > 0) dp[i] = max(dp[i],cnt + dp[i^(1<<j)]);
                   else dp[i]=max(dp[i],0 - dp[i^(1<<j)]);
                }

            }

        }
        printf("%d
",dp[tot-1]);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/shimu/p/5796238.html