HDU 4778 Gems Fight! 【记忆化搜索+位操作】


Gems Fight!

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

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. Gdifferent 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 bagcannot 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 whichare 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, untilno S Gems of the same color remained in that cooker. Then X owns those newMagic Stones. When X gets one or more new Magic Stones, he/she will also get abonus turn. If X gets Magic Stone in a bonus turn, he will get another bonusturn. 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 moreMagic Stones than the opponent as possible.
  Now Alice gets the first turn, and she wants to know, if
both of them act theoptimal way, what will be the difference between the number of her MagicStones 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. Theirmeanings are mentioned above.
  Then B lines follow. Each line describes a bag in the following format:
  
  n c
1c2 ... cn
  
  It means that there are n Gems in the bag and their colors are color c
1,colorc2...and color cn respectively.
  0<=B<=21, 0<=G<=8, 0<n<=10, S < 20.
  Theremay be extra blank lines between cases. You can get more information from thesample input.
  Theinput 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 ofBob'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 Alicewill make a Magic Stone at the end of turn 3, thus get turn 4 and get all thethree Magic Stones.

 


【题意】


现在有B个背包,每个背包里有各种颜色的物品若干。总共有G种颜色。现在Alice和Bob轮流选择一个包裹并将其中的物品放到一个混合器中。如果混合器中某种颜色的物品超过了S件,那么每S件会融合出一个宝石,并获得额外一次选择背包的权利。问如果两个人足够聪明,且Alice先手,Alice获得的宝石减去Bob获得的宝石是多少。


【思路】


由于数据范围较小,可以考虑暴力DFS,但为了提高效率,用上记忆化搜索。


具体做法见代码注释。


#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn = 205;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;

int g,b,s;
int n;
int num[maxn];         //混合器中每种颜色物品的数目 
int dp[1<<22];         //dp[i]表示在状态为i时Alice能比Bob多拿的宝石数

struct node
{
    int c[10];         //某个背包里某种颜色物品的数量
}bag[22];

void init()
{
    int k=(1<<b);
    mst(dp,-1);
    dp[k-1]=0;        //所有背包都被使用过的最终状态
    mst(num,0);
    mst(bag,0);
}

int solve(int k)                //得到的宝石数
{
    int ans=0;
    for(int i=1;i<=g;i++)
    {
        num[i]+=bag[k].c[i];
        while(num[i]>=s)
        {
            ans+=num[i]/s;
            num[i]%=s;
        }
    }
    return ans;
}

int dfs(int now)
{
    if(dp[now]!=-1) return dp[now];
    int temp[10];
    int ans=-INF;
    for(int i=1;i<=g;i++)
    {
        temp[i]=num[i];
    }
    for(int i=0;i<b;i++)
    {
        int k=(1<<i);
        if(!(now&k))                 //这个背包没被用过
        {
            int total=solve(i);
            if(total>0)     ans=max(ans,total+dfs(now|k));   //下一轮还是这个人,故加上
            else            ans=max(ans,total-dfs(now|k));   //下一轮换了一个人,故减去
            for(int j=1;j<=g;j++)              //回溯,恢复原来的num值
            {
                num[j]=temp[j];
            }
        }
    }
    return dp[now]=ans;
}

int main()
{
    int x;
    while(~scanf("%d%d%d",&g,&b,&s)&&(s||b||g))
    {
        init();
        for(int i=0;i<b;i++)
        {
            scanf("%d",&n);
            while(n--)
            {
                scanf("%d",&x);
                bag[i].c[x]++;
            }
        }
        printf("%d\n",dfs(0));
    }
    return 0;
}








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值