HDU 6082 度度熊与邪恶大魔王 (DP)
Description
度度熊为了拯救可爱的公主,于是与邪恶大魔王战斗起来。
邪恶大魔王的麾下有\(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 \leqslant n \leqslant 100000\),\(1 \leqslant m \leqslant 1000\),\(1 \leqslant a[i] \leqslant 1000\),\(0 \leqslant b[i] \leqslant 10\),\(0 \leqslant k[i] \leqslant 100000\),\(0 \leqslant p[i] \leqslant 1000\)。
Output
对于每组测试数据,输出最小的晶石消耗数量,如果不能击败所有的怪兽,输出-1。
Sample Input
1 2
3 5
7 10
6 8
1 2
3 5
10 7
8 6
Sample Output
6
18
Solution
读完题首先一定会想到用贪心,对于每种怪物,选能造成伤害的即能当中性价比最高的去攻击。但马上就会发现,当怪物的剩余的生命值小于一次攻击造成的伤害的时候,最高性价比的攻击可能就不是最优策略了。
稍做分析就会发现,这其实是一个背包模型,\(m\)种技能对应\(m\)件物品,每种技能都有价值和花费,每个怪物的生命值是价值的下限。现在问题转化为\(m\)种物品,每种都可以取无穷多个,要在总价值大于等于下限的条件下让总花费尽量少。完全背包的变形。
由于怪物的防御力最大只有\(10\),考虑枚举防御力,此时的攻击力减防御力就是物品的价值。对于个防御力值进行一遍DP。
设共有\(n\)只怪物,怪物的生命值最大为\(maxa\),\(p[i]\)为第\(i\)种技能的攻击力,\(c[i]\)为第\(i\)种技能的花费。
状态:\(dp[k][i][j]\)表示防御力为\(k\),用前\(i\)个技能使攻击值大于等于\(j\)时的最小花费。
转移:
初始:\(dp[k][i][0] = 0, 0 \leqslant i \leqslant m\),\(dp[k][0][j] = \infty, 1 \leqslant j \leqslant maxa\)
目标:\(\sum_{i=1}^{n}{dp[b[i]][m][a[i]]}\)
由于内存和时间的限制,可以把dp数组的第一维去掉,还可以考虑将n只怪物按防御力从小到大排序。
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 5;
struct Tuple
{
int a, b;
} t[N];
int p[1001], c[1001];
int dp[1001][1001];
bool cmp(Tuple &a, Tuple &b)
{
return a.b < b.b;
}
int main()
{
int n, m;
while (~scanf("%d%d", &n, &m))
{
for (int i = 1; i <= n; i++) scanf("%d%d", &t[i].a, &t[i].b);
sort(t + 1, t + n + 1, cmp);
for (int i = 1; i <= m; i++) scanf("%d%d", c + i, p + i);
int l = 1;
ll ans = 0;
bool flag = false;
while (l <= n)
{
int k = t[l].b;
int r = l + 1;
while (r <= n && t[r].b == k) r++;
int maxa = 0;
for (int i = l; i < r; i++) maxa = max(maxa, t[i].a);
for (int j = 1; j <= maxa; j++) dp[0][j] = INF;
for (int i = 0; i <= m; i++) dp[i][0] = 0;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= maxa; j++)
{
dp[i][j] = dp[i - 1][j];
if (p[i] - k > 0) dp[i][j] = min(dp[i][j], dp[i][max(j - p[i] + k, 0)] + c[i]);
}
for (int i = l; i < r; i++)
{
if (dp[m][t[i].a] == INF) { flag = true; break; }
ans += dp[m][t[i].a];
}
if (flag) break;
l = r;
}
if (flag) printf("-1\n");
else printf("%I64d\n", ans);
}
return 0;
}