【题目链接】
【题目考点】
1. 动态规划:二维费用背包
【解题思路】
记y[i]
:第i瓶的氧容量 d[i]
:第i瓶的氮容量 w[i]
:第i瓶的重量
1. 状态定义
- 集合:选择气缸的方案
- 限制:在前几个气缸中选择,需要满足至少多少氮气和氧气的容量
- 属性:总重量
- 条件:最小
- 统计量:总重量
状态定义:dp[l][i][j]
:前l个气缸中选气缸,拥有至少i升氧气容量与j升氮气容量的方案中,总重量最少的方案中气缸的总重量。
初始状态:由于要求的是最小值,整个数组先初始化为无穷大。
在前0个气缸中选气缸,只能满足至少有0升氧气与0升氮气,总重量为0。所以dp[0][0][0] = 0
。
2. 状态转移方程
与01背包的思路一样,只不过比01背包多了一个费用维度
-
集合:前l个气缸中选气缸,拥有至少i升氧气容量与j升氮气容量的方案
分割集合:依据是否选择第l气缸进行分割 -
子集1:如果选择第l气缸,那么接下来需要在前l-1个气缸中选择气缸,这些气缸应该拥有至少
i-y[l]
升氧气与j-d[l]
升氮气。
如果i-y[l]
为负数,“这些气缸应该拥有至少负几升的氧气容量”这样的表述没有意义,由于气缸的氧气容量最小为0,因此该表述等价于“这些气缸应该拥有至少0升的氧气容量”。
整合i-y[l]
为非负数或负数的情况,应该说:这些气缸应该拥有至少max(0, i-y[l])
升的氧气容量。同理,这些气缸应该拥有至少max(0, j-d[l])
升的氮气容量。
。
其最小总重量为dp[l-1][max(0, i-y[l])][max(0, j-d[l])]
,再加上第l气缸的重量w[l]
,得到:dp[l][i][j] = dp[l-1][max(0, i-y[l])][max(0, j-d[l])]+w[l]
。 -
子集2:如果不选第l气缸,那么接下来需要在前l-1个气缸中选择气缸,这些气缸应该拥有至少i升氧气与j升氮气,其最小总重量为
dp[l][i][j] = dp[l-1][i][j]
。 -
以上两种情况取最小值
【题解代码】
解法1:二维费用背包 基本解法
#include <bits/stdc++.h>
using namespace std;
#define N 1005
int m, n, k, dp[N][25][80];//dp[i][j][k]: 前i个气缸中选气缸凑j升氧气k升氮气的最少重量
int y[N], d[N], w[N];//y[i]:第i瓶的氧容量 d[i]:第i瓶的氮容量 w[i]:第i瓶的重量
int main()
{
memset(dp, 0x3f, sizeof(dp));
cin >> m >> n >> k;
dp[0][0][0] = 0;
for(int i = 1; i <= k; ++i)
cin >> y[i] >> d[i] >> w[i];
for(int l = 1; l <= k; ++l)
for(int i = 0; i <= m; ++i)
for(int j = 0; j <= n; ++j)
dp[l][i][j] = min(dp[l-1][max(0, i-y[l])][max(0, j-d[l])]+w[l], dp[l-1][i][j]);
cout << dp[k][m][n];
return 0;
}
解法2:二维费用背包 滚动数组优化
#include <bits/stdc++.h>
using namespace std;
#define M 100
#define N 1005
int m, n, k, dp[M][M];//dp[i][j][k]:(i被优化掉) 前i个气缸中选气缸凑j升氧气k升氮气的最少重量
int y[N], d[N], w[N];//y[i]:第i瓶的氧容量 d[i]:第i瓶的氮容量 w[i]:第i瓶的重量
int main()
{
memset(dp, 0x3f, sizeof(dp));
cin >> m >> n >> k;
dp[0][0] = 0;
for(int i = 1; i <= k; ++i)
cin >> y[i] >> d[i] >> w[i];
for(int l = 1; l <= k; ++l)
for(int i = m; i >= 0; --i)
for(int j = n; j >= 0; --j)
dp[i][j] = min(dp[max(0, i-y[l])][max(0, j-d[l])]+w[l], dp[i][j]);
cout << dp[m][n];
return 0;
}