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