zoukankan      html  css  js  c++  java
  • Aizu 2155 Magic Slayer 背包DP

    这是上上次对抗赛的题目了

    其实现在发现整个代码从头到尾,都是用了背包,怪我们背包没深入学好。

    比赛的时候,聪哥提出的一种思路是,预处理一下,背包出 ALL攻击 和 single攻击的 血量对应的最小花费,其实这些都没什么问题。。。主要是后面的问题,后面为了找到如何使用ALL攻击是最好的,我们是这样处理的,对怪物血量 升序排序,然后枚举,从哪个点开始,该点前面的怪物都用ALL杀死,后面的怪物都用single杀死,因为血高的放在后面多承受几次ALL攻击应该是最优的,这样看起来好像是对的。。也过了样例,就是WA了。。。。其实WA的很明显,我们居然三个人都没想到,刚刚重新敲这道题才发现这个策略大错特错了,我们这样枚举,很明显,没有计算,用了ALL攻击,但是没有杀死怪物的情况,也许这些就是最优解。。我们的策略,要么就不用ALL攻击,用了ALL攻击就一定要把怪物杀死。。。肯定有问题啊。

    后来还是参考的别人的比较好的思路,前面的处理是一样的,不过换了一下,背包出 两种攻击 的 花费 对应的 最大攻击,即 下标是 花费,值是攻击力,这样便于后面的处理。

    背包完之后,从0开始往上枚举 出 使用ALL的花费情况,然后就得到ALL的攻击总量,再遍历一遍怪物,就可以得到剩余血量用single攻击的花费,全部加起来就是可能的结果,全部枚举完就能求出最优解

    刚刚还和聪哥讨论了好久,为什么枚举ALL花费情况就可以得到所有合理的ALL攻击组合,这其实就是利用了背包的特性,即,我给你一个上限,就能帮我求出这个上限中的最优组合,就是利用了背包的特性。。。所以我为什么说这整个题目就是一个背包题,全部都在利用背包的特性。。怪我没有对背包理解透彻,这种隐藏的可以枚举花费,通过背包得到组合情况没有想到。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 110010
    using namespace std;
    int hp[110];
    
    struct weapon{
        int c,p;
    }all[110],sig[110];
    int dp[2][N];
    int all_num,sig_num;
    int m,n;
    void init()
    {
        all_num=sig_num=0;
    }
    void proc()
    {
        memset(dp,0,sizeof dp);
        for (int i=0;i<all_num;i++){
           for (int j=all[i].c;j<N;j++){
             dp[0][j]=max(dp[0][j],dp[0][j-all[i].c]+all[i].p);
           }
        }
        for (int i=0;i<sig_num;i++){
            for (int j=sig[i].c;j<N;j++){
                dp[1][j]=max(dp[1][j],dp[1][j-sig[i].c]+sig[i].p);
            }
        }
    }
    int bs(int val)
    {
        int l=0,r=N-1,mid;
        while (l<r)
        {
            mid=(l+r)>>1;
            if (dp[1][mid]<val) l=mid+1;
            else  r=mid;
        }
        return l;
    
    }
    int main()
    {
        char ch[20],cc[10];
        int a,b;
        while (scanf("%d",&n)){
            if (!n) break;
            init();
            for (int i=0;i<n;i++) scanf("%d",&hp[i]);
            scanf("%d",&m);
            bool flag=false;
            for (int i=0;i<m;i++){
                scanf("%s %d %s %d",ch,&a,cc,&b);
               // cout<<a<<" "<<b<<endl;
                if (cc[0]=='A') all[all_num++]=(weapon){a,b};
                if (cc[0]=='S') sig[sig_num++]=(weapon){a,b};
                if (a==0 && b>0) flag=1;
            }
            if (flag) {puts("0");continue;}
            proc();
            int ans=N*99;
            for (int i=0;i<ans;i++){
                int temp=i;
                for (int j=0;j<n;j++){
                    temp+=bs(hp[j]-dp[0][i]);
                }
                ans=min(ans,temp);
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    写代码如坐禅:你是哪一类程序员
    关于鸿蒙的商业讨论
    为什么你总是“把天聊死”?
    生活不易,唯有努力
    如何用一句话激怒一名程序员?
    华为正式开源方舟编译器,开源了,它真的开源了!
    为什么HTTPS比HTTP更安全?
    《管理者必读12篇》购买方法
    程序员都在用的电脑小技巧,看一遍就学会,每天早下班一小时
    一位程序员的一天工作清单:5:30下班,5:30起床
  • 原文地址:https://www.cnblogs.com/kkrisen/p/3754151.html
Copyright © 2011-2022 走看看