题意:有两个人在玩"Gems Fight!"……有B个包,有G种颜色的宝石,这两个人轮流选择某一个包,把这个包里的宝石放到一个共享的熔炉里,当这个熔炉里某一种颜色的宝石大于等于S,那么就可以产生一个魔法石,这个人得到这个魔法石并且还能得到一个额外的回合,两个人都用最佳策略,问最后两个人能获得的魔法石的差是多少。
思路:看到包不多,只有21个,那么可以考虑状压,用一个二进制数表示已经选择的包,则dp[state]表示在state的状态下,先手所能获得的最大值。由于每个状态下的宝石剩余的数量是可以确定的(因为到S就会被去掉),所以可以预处理一下每个状态下剩余的每个颜色的宝石的数量,接下来在当前状态下选择某一个包,看是否能产生出魔法石,如果能产生,那么他还可以在进行一个回合,所以dp[state]=max(dp[state],pt+f(state|(1<<i))) (pt是当前产生魔法石的数量),如果不能,则下一回合是对方选择,所以dp[state]=max(dp[state],pt-f(st|(1<<i)))。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #define inf 0x3f3f3f3f #define Inf 0x3FFFFFFFFFFFFFFFLL #define eps 1e-9 #define pi acos(-1.0) using namespace std; typedef long long ll; const int maxn=1<<21; int color[10],bag[25][15],G,B,S; int dp[maxn],remain[maxn][10]; bool vis[maxn]; int f(int st) { if(vis[st]) return dp[st]; if(st==((1<<B)-1)) { return 0; } vis[st]=true; int &res=dp[st]; res=inf; for(int i=0;i<B;++i) { if(!(st&(1<<i))) { int pt=0; for(int j=1;j<=bag[i][0];++j) remain[st][bag[i][j]]++; for(int j=0;j<=G;++j) pt+=remain[st][j]/S; for(int j=1;j<=bag[i][0];++j) remain[st][bag[i][j]]--; if(pt) { if(res==inf) res=pt+f(st|(1<<i)); else res=max(res,pt+f(st|(1<<i))); } else { if(res==inf) res=pt-f(st|(1<<i)); else res=max(res,pt-f(st|(1<<i))); } } } return res; } int main() { //freopen("a.txt","r",stdin); //freopen("out.txt","w",stdout); while(~scanf("%d%d%d",&G,&B,&S)) { if(G==0&&B==0&&S==0) break; for(int i=0;i<B;++i) { scanf("%d",&bag[i][0]); for(int j=1;j<=bag[i][0];++j) scanf("%d",&bag[i][j]); } memset(remain,0,sizeof(remain)); int total=1<<B,tmp; for(int i=0;i<total;++i) { tmp=i; int j=0; while(tmp) { if(tmp&1) { for(int k=1;k<=bag[j][0];++k) remain[i][bag[j][k]]++; } tmp>>=1; j++; } for(j=0;j<=G;++j) remain[i][j]%=S; } memset(vis,0,sizeof(vis)); int ans=f(0); printf("%d\n",ans); } return 0; }