二维费用背包的概念
概念:二维费用背包和前面的01背包等背包问题比较相似,只不过把我们的花费,设置成了两个,也就是我们多了一层限制。对于01背包来说就是多了一个体积。
母题(二维费用背包)
二维费用背包
题意:给我们N件物品,每件物品都有自己的体积重量个价值,然后一个体积是V并且最大承受重量是W的背包,问我们最多在不超过背包限制的情况下我们可以拿到的最大的价值是多少。
思路:
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int f[N][N];
int main()
{
cin.tie(0)->sync_with_stdio(false);
int n, v, m;
cin >> n >> v >> m;
for (int i = 1; i <= n; i++)
{
int a, b, c;
cin >> a >> b >> c;
for (int j = v; j >= a; j--)
for (int k = m; k >= b; k--)
f[j][k] = max(f[j][k], f[j - a][k - b] + c);
}
cout << f[v][m] << endl;
return 0;
}
接下来我们看一下变形
例题一(宠物之小精灵收服)
宠物之小精灵收服
题意:小智和皮卡丘要去收服小精灵,收服小精灵的话要花费一个精灵球,当小智决定要收服一个小精灵的话他会扔出一个精灵球然后派皮卡丘去对战,然后皮卡丘会受到小精灵的伤害,我们在皮卡丘血量不为0并且还剩余精灵球的情况下我们才能收服这个小精灵。问我们最多可以收服多少个小精灵,并且皮卡丘剩余的最大血量是多少。
思路:和上面那个题基本是一样的,但是有一个不一样的点就是我们从后往前循环的时候不能将将生命值从M开始循环,因为题目中要求一旦皮卡丘的血量为0的话我们就代表收服不成功。
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const int N = 1e3 + 10;
int f[N][N];
int main()
{
cin.tie(0)->sync_with_stdio(false);
int N, M, K;
cin >> N >> M >> K;
for (int i = 1; i <= K; i++)
{
int v1, v2;
cin >> v1 >> v2;
for (int j = N; j >= v1; j--)
for (int k = M - 1; k >= v2; k--)
f[j][k] = max(f[j][k], f[j - v1][k - v2] + 1);
}
cout << f[N][M - 1] << ' ';
int res = M - 1;
for (int i = 0; i <= M - 1; i++)//我们遍历一下血量
//然后从,找出与最大价值相等前提下的最小血量减小的值。
if (f[N][M - 1] == f[N][i])
res = min(res, i);
cout << M - res << endl;
return 0;
}
例题二(潜水员)
潜水员
题意:给我们一个潜水员下潜所需要的最小的氧气含量和氮气含量,然后给我们k个气缸,然后每个气缸都有氧气、氮气、质量,让我们求在满足气体环境的前提下我们最小的质量和是多少。
思路:
那么我们在这里其实和前面普通的二维费用背包的定义其实是不同的,上面的定义都是不超过,但是我们这个题的定义是至少。这就出现一个不同点就是,在上面那种定义下,我们的负数是不合法的,但是这个题的定义里我们的负数也是合法的方案。负数是和0等价的。也就是说这个题我们是可以多拿的,假设有潜水员需要5的氧气、6的氮气,我们现在只有一个氧气是7的、氮气是8的、质量是10的一个气缸。按我们以前的选法是没法取到这个气缸的,但是实际上我们是可以多取的,我拿这么多,但是我多的气可以不使。(感觉还是有些说不清楚,附上一篇大佬题解)
神犇题解
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 22, M = 80;
int f[N][M];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, K;
cin >> m >> n >> K;
memset(f, 0x3f, sizeof f);
f[0][0] = 0;
for (int i = 1; i <= K; i++)
{
int a, b, c;
cin >> a >> b >> c;
for (int j = m; j >= 0; j--)
for (int k = n; k >= 0; k--)
f[j][k] = min(f[j][k], f[max(0,j - a)][max(0,k - b)] + c);
}
cout << f[m][n] << endl;
return 0;
}