2017百度之星1003度度熊与邪恶大魔王

问题描述:

度度熊为了拯救可爱的公主,于是与邪恶大魔王战斗起来。

邪恶大魔王的麾下有n个怪兽,每个怪兽有a[i]的生命值,以及b[i]的防御力。

度度熊一共拥有m种攻击方式,第i种攻击方式,需要消耗k[i]的晶石,造成p[i]点伤害。

当然,如果度度熊使用第i个技能打在第j个怪兽上面的话,会使得第j个怪兽的生命值减少p[i]-b[j],当然如果伤害小于防御,那么攻击就不会奏效。

如果怪兽的生命值降为0或以下,那么怪兽就会被消灭。

当然每个技能都可以使用无限次。

请问度度熊最少携带多少晶石,就可以消灭所有的怪兽。

———-(吐槽百度的命题)

input:

本题包含若干组测试数据。

第一行两个整数n,m,表示有n个怪兽,m种技能。

接下来n行,每行两个整数,a[i],b[i],分别表示怪兽的生命值和防御力。

再接下来m行,每行两个整数k[i]和p[i],分别表示技能的消耗晶石数目和技能的伤害值。

数据范围:

1<=n<=100000

1<=m<=1000

1<=a[i]<=1000

0<=b[i]<=10

0<=k[i]<=100000

0<=p[i]<=1000


这道题是一道完全背包问题
dp[defend][j] 就表示防御为defend消耗血量为j需要的最小晶石(stone)
一边看代码一边理解吧,代码里有一定的注释

//首先,声明我进的一个坑,刚开始做的时候以为int就足够使用了
//所以stone(晶石)和dp数组就都是用的int,结果提交的时候一直wa,很无奈最后抱着试试的心态,改成了longlong,过~

#include<string.h>
#include<cstdio>
#include<vector>
#include<algorithm>


using namespace std;
//下面用了结构体,其实用数组完全可以,并且效率还会提升
struct bear    //度度熊
{
    int k;
    int p;
};
struct monster
{
    int a, b;
};
int main(void)
{
    freopen("in.txt", "r", stdin);    //重定向,提交时一定要删掉啊
    int n, m;
    long long dp[11][1010];
    while(scanf("%d%d", &n, &m)==2)
    {
        memset(dp, 0x3f, sizeof(dp));
        int att_max=0, def_max=0;
         int each_min[11];    //这里我命名有点问题,这个each_min是表示,每一个防御值里血量的最大值,可以减少循环的次数
         memset(each_min, 0, sizeof(each_min));
        vector<bear> be_ve;
        vector<monster> mon[11];  
        monster temp;
        for(int i=0;i<n;++i)
        {
            scanf("%d%d", &temp.a , &temp.b);
            if(def_max<temp.b)     
                def_max=temp.b;     //求得防御的最大值
            if(each_min[temp.b]<temp.a)
                each_min[temp.b]=temp.a;
            mon[temp.b].push_back(temp);
        }
        bear be;
        for(int i=0;i<m;++i)
        {
            scanf("%d%d",&be.k, &be.p);
            if(att_max<be.p)
                att_max=be.p;     //攻击的最大值
            be_ve.push_back(be);
        }
        if(att_max<=def_max)    //攻击的最大值小于等于防御  就无法攻破
            printf("-1\n");
        else
        {
            for(int defend=0;defend<11;defend++)
            {
                dp[defend][0]=0;

                for(int j=1;j<=each_min[defend];++j)
                {
                    for(int s=0;s<m;++s)
                    {
                        if(be_ve[s].p>defend)
                        {
                            if((j>=be_ve[s].p-defend))
                            {
                                dp[defend][j]=min(dp[defend][j], dp[defend][j-be_ve[s].p+defend]+be_ve[s].k);     //状态转移方程
                            }
                            else
                            {
                                if(dp[defend][j]>be_ve[s].k)
                                    dp[defend][j]=be_ve[s].k;      //这里需要注意一下,当攻击大于防御的时候,可能攻击一下,就干掉了小怪兽,这里遍历找到一下就能打倒小怪兽的最小晶石
                            }
                        }
                    }
                }
            }
            long long stone=0;    //我差点在这里翻车,以后要长记性了,充分考虑数 的范围
            for(int i=0;i<11;++i)
            {
                for(int j=0;j<mon[i].size();++j)
                {
                    stone+=dp[i][mon[i][j].a];    //防御为i血量为a的monster消耗的最小晶石,把每一个monster都遍历一下,在dp中找到并加在一起,就OK了
                }
            }
            printf("%I64d\n", stone);
        }
    }
    return 0;
}

只要背包问题理解的深刻,这个问题也并不难解决,欢迎留言~

猜你喜欢

转载自blog.csdn.net/qq_27854685/article/details/76780071