zoukankan      html  css  js  c++  java
  • 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;
    }
    



  • 相关阅读:
    阿里播放器踩坑记录 进度条重构 video loadByUrl失效解决方案
    liunx 安装nc/netcat centos安装netcat
    jquery实现显示textarea输入字符数
    SQL 时间戳转换为日期
    .Net WebRequest异步请求与WebClient异步请求
    SQL删除多列语句
    jQuery为元素设置css的问题
    关于调试WCF时引发的异常XmlException: Name cannot begin with the '<' character, hexadecimal value 0x3C” on Client Side
    SQL删除指定条件的重复数据,只保留一条
    net.exe use命令的使用
  • 原文地址:https://www.cnblogs.com/herumw/p/9464612.html
Copyright © 2011-2022 走看看