AcWing 1022. 宠物小精灵之收服
\(AcWing\) \(1022\). 宠物小精灵之收服
一、题目描述
宠物小精灵是一部讲述小智和他的搭档皮卡丘一起冒险的故事。
一天,小智和皮卡丘来到了小精灵狩猎场,里面有很多珍贵的野生宠物小精灵。
小智也想收服其中的一些小精灵。
然而,野生的小精灵并不那么容易被收服。
对于每一个野生小精灵而言,小智可能需要使用很多个精灵球才能收服它,而在收服过程中,野生小精灵也会对皮卡丘造成一定的伤害(从而减少皮卡丘的体力)。
当皮卡丘的体力小于等于\(0\)时,小智就必须结束狩猎(因为他需要给皮卡丘疗伤),而 使得皮卡丘体力小于等于\(0\)的野生小精灵也不会被小智收服。
当小智的精灵球用完时,狩猎也宣告结束。
我们假设小智遇到野生小精灵时有两个选择:收服它,或者离开它。
如果小智选择了收服,那么一定会扔出能够收服该小精灵的精灵球,而皮卡丘也一定会受到相应的伤害;如果选择离开它,那么小智不会损失精灵球,皮卡丘也不会损失体力。
小智的目标有两个:主要目标是收服尽可能多的野生小精灵;如果可以收服的小精灵数量一样,小智希望皮卡丘受到的伤害越小(剩余体力越大),因为他们还要继续冒险。
现在已知小智的精灵球数量和皮卡丘的初始体力,已知每一个小精灵需要的用于收服的精灵球数目和它在被收服过程中会对皮卡丘造成的伤害数目。
请问,小智该如何选择收服哪些小精灵以达到他的目标呢?
输入格式
输入数据的第一行包含三个整数:\(N,M,K\),分别代表小智的精灵球数量、皮卡丘初始的体力值、野生小精灵的数量。
之后的\(K\)行,每一行代表一个野生小精灵,包括两个整数:收服该小精灵需要的精灵球的数量,以及收服过程中对皮卡丘造成的伤害。
输出格式
输出为一行,包含两个整数:\(C,R\),分别表示最多收服\(C\)个小精灵,以及收服\(C\)个小精灵时皮卡丘的剩余体力值最多为\(R\)。
数据范围
\(0<N≤1000,0<M≤500,0<K≤100\)
输入样例1:
10 100 5
7 10
2 40
2 50
1 20
4 20
输出样例1:
3 30
输入样例2:
10 100 5
8 110
12 10
20 10
5 200
1 110
输出样例2:
0 100
二、题目解析
分析
本题是一道 01背包 的扩展题 —— 二维费用\(01\)背包问题
把 野生的小精灵 看做物品,则捕捉他需要的 精灵球 个数就是第一费用,皮卡丘要掉的血就是 第二费用
最后答案要求 物品数量 最多,因此我们可以用 状态的属性 来表示 选择的物品数
闫氏DP分析法
初始状态:f[0][0][0]
目标状态:f[n][m][t - 1]
(皮神不能的血不能全部流光,否则它就挂了~)
三、实现代码
时间复杂度:\(O(n×m×k)\)
#include <bits/stdc++.h>
using namespace std;
const int N = 110; // 野生小精灵的数量
const int M1 = 1010; // 小智的精灵球数量
const int M2 = 510; // 皮卡丘的体力值
int n, m1, m2;
int f[M1][M2]; // 一维:精灵球数量,二维:皮卡丘的体力值,值:抓到的小精灵数量最大值
int main() {
cin >> m1 >> m2 >> n;
m2--; // 留一滴血
// 二维费用01背包
// 降维需要将体积1、体积2倒序枚举
for (int i = 1; i <= n; i++) {
int v1, v2;
cin >> v1 >> v2;
for (int j = m1; j >= v1; j--)
for (int k = m2; k >= v2; k--)
f[j][k] = max(f[j][k], f[j - v1][k - v2] + 1); // 获利就是多了一个小精灵
}
// 最多收服多少个小精灵[在消耗精灵球、血极限的情况下,肯定抓的是最多的,这不废话吗]
printf("%d ", f[m1][m2]);
// 找到满足最大价值的所有状态里,第二维费用消耗最少的
int cost = m2;
for (int i = 0; i <= m2; i++) // 如果一个都不收服,则体力消耗最少,消耗值为0
if (f[m1][i] == f[m1][m2])
cost = min(cost, i);
// 收服最多个小精灵时皮卡丘的剩余体力值最大是多少
printf("%d\n", m2 + 1 - cost);
return 0;
}
四、二维费用\(01\)背包练习题
实现代码
#include <bits/stdc++.h>
using namespace std;
int f[1010][1010];
/*
测试数据:
4 5 6
1 2 3
2 4 4
3 4 5
4 5 6
答案:
8
*/
int n, V, M;
int main() {
scanf("%d %d %d", &n, &V, &M); //物品件数 背包容量 背包承重
for (int i = 1; i <= n; i++) { //枚举物品
int v, m, w; //体积 重量 价值
scanf("%d %d %d", &v, &m, &w);
for (int j = V; j >= v; j--) //枚举体积
for (int k = M; k >= m; k--) //枚举承重
f[j][k] = max(f[j][k], f[j - v][k - m] + w);
}
printf("%d\n", f[V][M]);
return 0;
}