zoukankan      html  css  js  c++  java
  • 【HDU4778】Gems Fight!-状态压缩DP+博弈

    测试地址:Gems Fight!
    题目大意:B(21)包宝石,宝石共有G(8)种颜色,双方每轮取走一包并放在一个共用的容器里,如果一个玩家取完一包后,该容器内有S(<20)个同色宝石,则可以将它们合成为一块魔法石并收入囊中,一直合成直到无法合成为止。另外,如果一轮后该玩家合成了至少一块魔法石,那么他可以获得额外的一轮,如果又合成了魔法石就又获得额外的一轮,以此类推。玩家的目标是获得尽量多的魔法石。现在的问题是,若双方都采用最佳策略,先手获得的魔法石数减去后手获得的魔法石数的数为多少。
    做法:本题需要用到状态压缩DP+博弈+记忆化搜索。
    这是一道好题啊…通过分析,发现一个状态仅受后面的状态影响,而且B很小,于是考虑状态压缩DP。
    f(s)为包被取的状态为s的情况下,先手获得的魔法石数减去后手获得的魔法石数的最大值,那么枚举每一个s中为0的位,查看选走该包后能不能合成新的魔法石。
    如果能,那么下一轮还是先手取,所以这种情况的状态转移方程为f(s)=max(f(s),f(next)+add),其中next为选走该包后的状态,add为新增的魔法石数。
    如果不能,那么下一轮变为后手取,那么这种情况的状态转移方程就是:f(s)=max(f(s),f(next))
    注意f(s)最优值可能为负,所以算法一开始应初始化f(s)inf,边界条件为f(2B1)=0。这样我们就解决了这个问题,时间复杂度为O(BG2B),时限比较宽,所以能够通过。
    下列代码是用记忆化搜索的写法写的,看着比较清楚易懂。
    以下是本人代码:

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define inf 1000000000
    using namespace std;
    int g,b,s,n;
    int c[25][11],f[3000010];
    bool vis[3000010]={0};
    
    int dp(int x)
    {
        if (vis[x]) return f[x];
        if (x==(1<<b)-1) return 0;
        int v=x,i=1,cl[10]={0},add=0;
        f[x]=-inf;
        while(v)
        {
            if (v&1)
            {
                for(int j=1;j<=g;j++)
                {
                    cl[j]+=c[i][j];
                    cl[j]%=s;
                }
            }
            v>>=1,i++;
        }
        for(i=1;i<=b;i++)
            if (!(x&(1<<(i-1))))
            {
                add=0;
                for(int j=1;j<=g;j++)
                    add+=(cl[j]+c[i][j])/s;
                if (add>0) f[x]=max(f[x],dp(x+(1<<(i-1)))+add);
                else f[x]=max(f[x],-dp(x+(1<<(i-1))));
            }
        vis[x]=1;
        return f[x];
    }
    
    int main()
    {
        while(scanf("%d%d%d",&g,&b,&s)&&g&&b&&s)
        {
            memset(c,0,sizeof(c));
            memset(vis,0,sizeof(vis));
            for(int i=1;i<=b;i++)
            {
                scanf("%d",&n);
                while(n--)
                {
                    int a;
                    scanf("%d",&a);
                    c[i][a]++;
                }
            }
            printf("%d
    ",dp(0));
        }
    
        return 0;
    }
    
  • 相关阅读:
    css3很美的蟠桃动画
    EXCEL countif函数多条件
    R语言介绍
    【转】Docker:Docker Compose 详解
    【转】Linux基础命令-curl
    【转】进入正在运行的Docker容器的4种方式
    【转】yum和apt-get的区别
    【转】laravel框架中 session保存到Redis中
    【转】Laravel 控制器 Controller 传值到 视图 View 的几种方法总结
    【转】Laravel中使用门面模式(Facade)
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793583.html
Copyright © 2011-2022 走看看