现在的位置: 首页 > 综合 > 正文

hdu 4778 Gems Fight! 2013 Asia Hangzhou Regional Contest

2018年04月25日 ⁄ 综合 ⁄ 共 2483字 ⁄ 字号 评论关闭

这一题特别像大白书Ch1的 ex.28 Sum游戏,臣妾对着这个example+看了一堆题解终于把这道博弈DP想清楚了%>_<%
首先要注意这一题里面总的score是不变的,因为bag都放在同一个cooker里,magic stone要么给A要么给B
用二进制数表示当前状态,1001表示A or B有第一个和第四个bag可以选,第二个和第三个bag之前已经选过了(即不可选)所以对应的score[1001]应该计算把第二个和第三个bag放到cooker可以得到的score,我开始把这个弄反了==算成了第一个和第四个的score==
dp[state]表示当前状态(有哪些bag可选,有哪些bag已经选过了)开始博弈,到最后先手得分的最大值。
状态转移时,假设state对应的是A是先手,如果state&(1<<i)!=0,则说明bag i可选,下一个状态就是newstate=state^(1<<i),newstate其实就是将state里面的i位从1变到0,表示已经选过了bag i。
改变的分数就是val=score[newstate]-score[state]
如果val>0,那么下一步还是A,所以dp[state]=max(dp[state],val+dp[newstate])
如果选bag i无法使得A加分,那么下一步就换成B是先手,所以dp[newstate]就表示B是先手,选到最后,B得分最大值,因为总分数一定的,所以A的分数=state状态下对应的总分数-dp[下一个回合B先出手] 
state状态下对应的总分数=score[0]-score[state],假设state=1001,那么现在还可以把bag 1 and bag 4放进去,而score[1001]表示把bag 2 和bag 3 放进cooker的得分,所以要用total score相减
因为一直取max,就可以保证是最优的。
这里面状态转移不会转移到非法的状态,所以记忆化搜索里面也木有非法状态的特判。

最后的分数=dp[(1<<b)-1]-(score[0]-dp[(1<<b)-1]),score[0]表示total score

DP过程中先手后手对应的是A还是B是会变的==

#include<iostream>
#include<stdio.h>
#include<cstdio>
#include<stdlib.h>
#include<vector>
#include<string>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<queue>
#include <ctype.h>
#include<map>
#include<time.h>
using namespace std;
//hdu 4778

const int maxn=1<<21;
int b;
int g;
int n;
int s;
int c[22][12];
int score[maxn];
int sum[20];
int dp[maxn];
int vis[maxn];
void caltotal()//计算cooker中每个状态的score,=0表示已经选过了放进了cooker
{
    for(int m=0;m<(1<<b);m++)
    {
        memset(sum,0,sizeof(sum));
        for(int i=0;i<b;i++)
        {
            if((m&(1<<i))==0)
            {
                for(int j=1;j<=g;j++)
                {
                    sum[j]+=c[i][j];
                }
            }
        }
        for(int i=1;i<=g;i++)
        {
            score[m]+=sum[i]/s;
        }
    }
}
int dfs(int state)
{
    if(vis[state])
    {
        return dp[state];
    }
    vis[state]=1;
    int ret=0;
    for(int i=0;i<b;i++)
    {
        if(state&(1<<i))//=1表示当前状态已经存在bag i,所以i可选。向选过bag i的状态转移(所以newstate的i位是0)
        {
            int newstate=state^(1<<i);
            int val=score[newstate]-score[state];
            if(val>0)
            {
                ret=max(ret,val+dfs(newstate));
            }
            else
            {
                ret=max(ret,(score[0]-score[state])-dfs(newstate));
                //所有bag都放到cooker中的score-当前状态还有某些包可选对应的score(只放了一部分bag到cooker里的score)=s当前状态的总分数
            }
        }
    }
    return dp[state]=ret;//一定要赋值才能实现记忆化搜索!
}
int main()
{
    freopen("input.txt","r",stdin);
    //freopen("data.txt","r",stdin);
    //freopen("out1.txt","w",stdout);
    while(true)
    //while(scanf("%d %d %d",&g,&b,&s),g|b|s)
    {
        scanf("%d %d %d",&g,&b,&s);
        if(!b&&!g&&!s) break;
        memset(score,0,sizeof(score));
        memset(c,0,sizeof(c));
        memset(dp,0,sizeof(dp));
        memset(vis,0,sizeof(vis));
        for(int i=0;i<b;i++)
        {
            scanf("%d",&n);
            for(int j=0;j<n;j++)
            {
                int color=0;
                scanf("%d",&color);
                c[i][color]++;
            }
        }
        caltotal();
        int ans=dfs((1<<b)-1)*2-score[0];//0表示已经选过放在cooker中了,所以score[0]是总的分数
        //dp[11...11]-(total score-dp[11...11])
        printf("%d\n",ans);
    }
     return 0;
}



抱歉!评论已关闭.