hdu4778 Gems Fight!

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 327680/327680 K (Java/Others)
Total Submission(s): 1912    Accepted Submission(s): 824


Problem Description
  Alice and Bob are playing "Gems Fight!":
  There are Gems of G different colors , packed in B bags. Each bag has several Gems. G different colors are numbered from color 1 to color G.
  Alice and Bob take turns to pick one bag and collect all the Gems inside. A bag cannot be picked twice. The Gems collected are stored in a shared cooker.
  After a player ,we name it as X, put Gems into the cooker, if there are S Gems which are the same color in the cooker, they will be melted into one Magic Stone. This reaction will go on and more than one Magic Stone may be produced, until no S Gems of the same color remained in that cooker. Then X owns those new Magic Stones. When X gets one or more new Magic Stones, he/she will also get a bonus turn. If X gets Magic Stone in a bonus turn, he will get another bonus turn. In short,a player may get multiple bonus turns continuously.
  There will be B turns in total. The goal of "Gems Fight!" is to get as more Magic Stones than the opponent as possible.
  Now Alice gets the first turn, and she wants to know, if both of them act the optimal way, what will be the difference between the number of her Magic Stones and the number of Bob's Magic Stones at the end of the game.
 

Input
  There are several cases(<=20).
  In each case, there are three integers at the first line: G, B, and S. Their meanings are mentioned above.
  Then B lines follow. Each line describes a bag in the following format:
  
  n c1 c2 ... cn
  
  It means that there are n Gems in the bag and their colors are color c1,color c2...and color cn respectively.
   0<=B<=21, 0<=G<=8, 0<n<=10, S < 20.
  There may be extra blank lines between cases. You can get more information from the sample input.
  The input ends with G = 0, B = 0 and S = 0.
 

Output
  One line for each case: the amount of Alice's Magic stones minus the amount of Bob's Magic Stones.
 

Sample Input
3 4 3 2 2 3 2 1 3 2 1 2 3 2 3 1 3 2 2 3 2 3 1 3 1 2 3 0 0 0
 

Sample Output
3 -3
Hint
  For the first case, in turn 2, bob has to choose at least one bag, so that Alice will make a Magic Stone at the end of turn 3, thus get turn 4 and get all the three Magic Stones.
 
这是一题状压dp+记忆化搜索,因为每一个人都要走最优的方案,所以这样的方案只有一种,我们用二进制表示背包的状态,因为如果背包的状态确定的话,那么得到的总分数是一定的,如果一个人想赢,一定是尽可能的使得自己的得分大于对方,即分差尽量大,可以从后往前推,这样推到当前状态才知道哪个是最优的,。可以用dp[state]表示当前背包状态是state时,先手与后手的最大分数差,这样就可以转移了。

#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
#define inf 99999999
int dp[4200000][2];
int vis[4200000];
int num[30][30];
int p[10];  //把p[]设为全局变量,这样dfs中的一个层改变,总体也改变了
int tot,g,b,s;

void dfs(int state,int turn)
{
    int i,j,st,turn1;
    int pp[10];
    if( state==(1<<b)-1 ){
        dp[state][0]=dp[state][1]=0;
        return;
    }
    if(vis[state])return;
    dp[state][1]=0;
    dp[state][0]=-inf;
    int cha=-inf;
    for(j=1;j<=8;j++){  //这里要先把p[]备份,因为还要回溯
        pp[j]=p[j];
    }
    for(i=1;i<=b;i++){
        int t1=0;
        int t2=0;
        if(state&(1<<(i-1)) )continue;  //这里要选择一个当前状态没有选择过的背包
        st=state|(1<<(i-1) );
        int cnt=0;
        for(j=1;j<=g;j++){
            p[j]+=num[i][j];
            cnt+=p[j]/s;
            p[j]%=s;
        }
        if(cnt==0){
            turn1=1^turn;  //这里表示是不是要换成对手拿
        }
        else turn1=turn;
        dfs(st,turn1);
        for(j=1;j<=8;j++){
            p[j]=pp[j];
        }
        t1+=cnt;
        if(cnt==0){   //如果交换了,那么先手t1的值要加上st状态后手拿的最大值
            t1+=dp[st][1];
            t2+=dp[st][0];
        }
        else{
            t1+=dp[st][0];
            t2+=dp[st][1];
        }
        if(t1-t2>cha){
            cha=t1-t2;
            dp[state][0]=t1;
            dp[state][1]=t2;
        }
    }
    vis[state]=1;   //访问过的状态就不用访问了,相当于剪枝
    return ;
}



int main()
{
    int n,m,i,j,c,t;
    while(scanf("%d%d%d",&g,&b,&s)!=EOF)
    {
        if(g==0 && b==0 && s==0)break;
        memset(num,0,sizeof(num));
        for(i=1;i<=b;i++){
            scanf("%d",&t);
            for(j=1;j<=t;j++){
                scanf("%d",&c);
                num[i][c]++;
            }
        }
        memset(p,0,sizeof(p));
        memset(vis,0,sizeof(vis));
        dfs(0,0);
        printf("%d
",dp[0][0]-dp[0][1]);
    }
    return 0;
}

也可以用状压dp,用dp[state]表示在state状态下先手与后手的最大差距。
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<string>
#include<algorithm>
using namespace std;
typedef long long ll;
typedef long double ldb;
#define inf 99999999
#define pi acos(-1.0)
#define maxn 15
int a[25][22],dp[1<<23];
int color[25],color1[25];


int main()
{
    int n,m,i,j,T,G,B,S,c,state;
    while(scanf("%d%d%d",&G,&B,&S)!=EOF)
    {
        if(G==0 && B==0 && S==0)break;
        memset(a,0,sizeof(a));
        for(i=1;i<=B;i++){
            scanf("%d",&n);
            for(j=1;j<=n;j++){
                scanf("%d",&c);
                a[i][c]++;
            }
        }
        dp[0]=0;
        for(state=1;state<(1<<B);state++){
            dp[state]=-inf;
            for(i=1;i<=G;i++)color[i]=0;
            for(i=1;i<=B;i++){
                if((state&(1<<(i-1) ))==0 ){
                    for(j=1;j<=G;j++){
                        color[j]+=a[i][j];
                        color[j]=color[j]%S;
                    }
                }
            }
            for(i=1;i<=B;i++){
                if(state&(1<<(i-1) )){
                    int state1=(state^(1<<(i-1) ) );
                    for(j=1;j<=G;j++)color1[j]=color[j];
                    int cnt=0;
                    for(j=1;j<=G;j++){
                        color1[j]+=a[i][j];
                        cnt+=color1[j]/S;
                    }
                    if(cnt!=0) dp[state]=max(dp[state],dp[state1]+cnt);
                    else dp[state]=max(dp[state],-dp[state1]);  //这里没有产生魔法石,所以先后手互换
                }
            }
        }
        printf("%d
",dp[(1<<B)-1]);
    }
    return 0;
}



原文地址:https://www.cnblogs.com/herumw/p/9464612.html