1. 程式人生 > >hdu 4778【狀態壓縮+記憶化搜尋】

hdu 4778【狀態壓縮+記憶化搜尋】

http://acm.hdu.edu.cn/showproblem.php?pid=4778

題意:現在有一些寶石,這些寶石有G種顏色,有B個袋子,這些寶石裝在袋子裡,給出每個袋子裡每種顏色寶石的數量,現在進行一個遊戲。

遊戲規則:兩個人輪流操作,每次操作選一個袋子,將其中的寶石全部放進一個容器裡面,如果容器中某種顏色的寶石的個數達到s,操作者獲得(num/s)個魔力寶石,剩下的寶石不會消失,如果某個回合得到的魔力寶石的數量大於0(至少有一個)這個人可以獲得一個新的回合(條件允許可以一直操作到所有寶石消耗完畢)。

問題:兩個人都採用最優策略,求(先手得到的魔力寶石數量-後手得到的魔力寶石數量),該值可以為負。

分析: 0<=B<=21, 0<=G<=8, 0<n<=10, S < 20。 由於B<=21,可以想到對袋子進行狀態壓縮,0表示袋子已經被取走,1表示未被取走。由於每個寶石的數量是給定的,最後所有袋子一定會被全部取完,那麼先手和後手所得到的魔力寶石的陣列總量是一定的。用dp[st]表示從st狀態開始,先手能獲得的最大魔力寶石數量。

設temp為選第i個袋子後能獲得的魔力寶石數量

當temp>0時,可以連續行動,dp[st]=max(dp[st-(1<<i)]+temp)

當temp=0時,下個回合時對手行動,dp[st]=max(未選取袋子能生成的魔力寶石數量-dp[st-(1<<i)])

例:st=010010   st-(1<<i)=010000;

temp大於0時,說明從010010狀態下,取走第i個袋子,可以連續行動,那麼dp[010010]=dp[010000]+temp

temp小於0時,說明從010010狀態下,取走第i個袋子,不能連續行動,那麼要求從010010狀態先手的情況,010000狀態下的先手就變成了後手,dp[010010]=left(未選取袋子能生成的魔力寶石數量)-dp[010000] 。

最後求差,用2*ans-sum即可。

#include"bits/stdc++.h"
using namespace std;
const int inf = -(1<<30);
int g,b,s,n;
int dp[1<<22];
int color[10];
int bag[30][10];
int dfs(int st,int left,int c[])
{
    if(left==0||st==0)return 0;
    if(dp[st]!=inf)return dp[st];
    int ans=0;
    for(int i=1;i<=b;i++)
    {
        int cnt=0;
        int x=1<<i;
        if(!(st&x))continue;
        int temp=0;
        int cc[10];//儲存容器裡面剩下的寶石
        for(int j=1;j<=g;j++)
        {
            temp+=(c[j]+bag[i][j])/s;
            cc[j]=(c[j]+bag[i][j])%s;
        }
        if(temp)
        {
            ans=max(ans,temp+dfs(st-x,left-temp,cc));
        }
        else ans=max(ans,left-dfs(st-x,left-temp,cc));
    }
    dp[st]=ans;
    return ans;
}
int main()
{
    while(~scanf("%d%d%d",&g,&b,&s)&&g)
    {
        for(int i=0;i<=1<<(b+1);i++)dp[i]=inf;
        memset(color,0,sizeof(color));
        memset(bag,0,sizeof(bag));
        for(int i=1;i<=b;i++)
        {
            scanf("%d",&n);
            int x;
            while (n--)
            {
                scanf("%d",&x);
                color[x]++;
                bag[i][x]++;
            }
        }
        int sum=0;
        for(int i=1;i<=g;i++) sum+=color[i]/s;
        int f[10];
        memset(f,0,sizeof(f));
        int ans=dfs((1<<(b+1))-1,sum,f);
        printf("%d\n",ans*2-sum);
    }

}