hdu 6082 度度熊与邪恶大魔王 背包

题目链接


题意:

有 n 个怪兽,每个怪兽各有 a[i] 的生命 与 b[i] 的防御;

有 m 种技能(可无限次使用),每种技能有 k[i] 的代价 与 p[i] 的攻击;

第 i 种技能 对 第 j 个怪兽造成的伤害为 攻击 - 防御,即 p[i] - b[j].

现要求消灭怪兽的最小代价。若不能消灭,输出 -1.


数据范围:

1<=n<=100000
1<=m<=1000
1<=a[i]<=1000
0<=b[i]<=10
0<=k[i]<=100000
0<=p[i]<=1000


思路:

显然,每个怪兽都是独立的。但是,对每个怪兽都做一次背包是不可承受的。

注意到,怪兽的防御值为 0 ~ 10,所以我们可以先将怪兽而防御值分组,对每个组整体去做背包。

按防御值分好了组后,对该组做背包时,每个技能的伤害就可以看做是定值了(该技能的攻击 - 该组怪物的防御),从而解决了伤害因怪物而异的问题。

做完背包后从后往前求后缀的最小值 minn[i],即为伤害大于等于 i 的最小代价,对于该组内的每个怪物直接加一加就好了。


另一个需要注意的地方是,因为伤害值可以超过怪物的生命值,所以背包的大小应该开到 怪物的最大的生命值 加上 技能最大的伤害值 这么大(最大也不过 2000)。显然,再大就没有必要了。


至于有怪物不能消灭输出 -1,那就是存在一组,所有技能的攻击值 均小于等于 该组的防御值。一开始漏看了这点还 WA 了一次...补上去就AC了也是心情好~


Code:

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3f
#define maxn 2010
#define maxm 1010
using namespace std;
typedef long long LL;
vector<int> hurt[12];
LL dp[maxn], minn[maxn];
int k[maxm], p[maxm], life[12], n, m;
inline int max(int a, int b) { return a > b ? a : b; }
inline LL min(LL a, LL b) { return a < b ? a : b; }
void work() {
    for (int i = 0; i <= 10; ++i) hurt[i].clear();
    memset(life, 0, sizeof(life));
    for (int i = 0; i < n; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        hurt[y].push_back(x);
        life[y] = max(life[y], x);
    }
    int stg = 0;
    for (int i = 0; i < m; ++i) { scanf("%d%d", &k[i], &p[i]); stg = max(stg, p[i]); }
    LL sum = 0;
    for (int i = 0; i <= 10; ++i) {
        if (hurt[i].empty()) continue;
        int lim = life[i] + stg;
        for (int j = 0; j <= lim; ++j) dp[j] = inf;
        dp[0] = 0;
        bool flag = false;
        for (int j = 0; j < m; ++j) {
            int atk = p[j] - i;
            if (atk <= 0) continue;
            flag = true;
            for (int kk = atk; kk <= lim; ++kk) dp[kk] = min(dp[kk], dp[kk - atk] + k[j]);
        }
        if (!flag) {
            printf("-1\n");
            return;
        }
        minn[lim] = dp[lim];
        for (int j = lim - 1; j >= 0; --j) minn[j] = min(minn[j + 1], dp[j]);
        for (auto x : hurt[i]) sum += minn[x];
    }
    printf("%lld\n", sum);
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) work();
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值