http://acm.hdu.edu.cn/showproblem.php?pid=4778
题目大意:有B个盒子里面放有G种颜色的宝石,两个人轮流选一个盒子将其中的宝石取出来放到一个锅里,然后其中没有S个相同颜色的宝石,它们就会聚合在一起变成一个魔法石(可能产生多个魔法石且锅里有可能有剩余的宝石),然后本轮的得分就是产生的魔法石的数量,且如果本轮某个人拿到了魔法石的话,那么下一轮他还可以继续选择盒子放入宝石,知道他在某一轮没有拿到魔法石,现在问两个人都采用最优策略的情况下,到最后先拿的那个人的得分与后拿的人的得分的差是多少。
思路:通过题意我们可以发现,所获得的的魔法石是一定的,也就是说不管按照什么顺序选择,到最后两人拿到的魔法石之和是一定的(这很容易算出来),然后可以发现B最多只有21个,那么我们可以通过状压来表示在某一个状态下先手最多可以拿多少个魔法石。我们可以通过记忆化搜索来解决这个问题,首先在某个特定的状态下,锅里所剩余的宝石是固定的,且还能拿到的魔法石的数量也是一定的。那么我们设dp[flag]表示状态是flag下先手最多可拿的魔法石数量,接下来就是状态转移了。
假设我们选择了第i个盒子((flag>>i)&1==1),则我们可以算可拿到多少个魔法石,设为x个,如果x>0,表示下一轮还是自己选,
则dp[flag]=max(dp[flag],dp[flag^(1<<i)]+x),
否则下一轮是对方选择,
则 dp[flag]=max(dp[flag],left-dp[flag^(1<<i)]),这里的left表示当前状态下还可以拿多少个魔法石。
最后我们求dp[(1<<b)-1]即可。最后还要注意我们要求的答案是两者得分之差,注意转换一下即可。
代码如下:
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> #define inf -2100000000 using namespace std; int dp[1<<21]; int g,b,s; int box[21][8],c[8]; int dfs(int flag,int left,int cc[]) { if(left==0||flag==0) return 0; if(dp[flag]!=inf) return dp[flag]; int i,tmp,ans=0,ben; int tt[8]; memset(tt,0,sizeof(tt)); for(i=b-1;i>=0;i--) { ben=0; if((flag>>i)&1) { tmp=flag^(1<<i); int sum=0; for(int j=0;j<g;j++) { tt[j]=(cc[j]+box[i][j]); sum+=tt[j]/s; tt[j]%=s; } if(sum) { ben=sum+dfs(tmp,left-sum,tt); } else { ben=left-dfs(tmp,left,tt); } ans=max(ans,ben); } } return dp[flag]=ans; } int main() { // freopen("dd.txt","r",stdin); while(scanf("%d%d%d",&g,&b,&s)) { if(!g) break; memset(box,0,sizeof(box)); memset(c,0,sizeof(c)); for(int i=0;i<b;i++) { int n,x; scanf("%d",&n); while(n--) { scanf("%d",&x); box[i][x-1]++; c[x-1]++; } } int sum=0; for(int i=0;i<g;i++) { sum+=c[i]/s; } int cc[8]; int limit=(1<<b); for(int i=0;i<limit;i++) { dp[i]=dp[i]=inf; } memset(cc,0,sizeof(cc)); int ans=dfs((1<<b)-1,sum,cc); printf("%d\n",2*ans-sum); } return 0; }