文章目录
0. 前言
相关:
1. 01背包+二维费用+思维
本题重在总结三种体积的不同状态表示方法,初始化及答案。
- 体积最多是
j
:大多数背包问题- 初始化: 假设为
f[0,i]
,表示一个物品都不选,且体积最大是i
的最大价值。那么就全部初始化为 0,因为所有物品都不选,那么体积不论是多少,其最大价值都是 0。这就只能求价值的最大值
- 初始化: 假设为
- 体积恰好是
j
:典型题:[完全背包] 买书(完全背包+裸题)
- 初始化:就只初始化
f[0][0]=0
,其余都初始化为INF
,这个是用来求价值的最小值。反之求最大值,其余都初始化为-INF
即可
- 初始化:就只初始化
- 体积至少是
j
:典型题:[01背包] 潜水员(01背包+二维费用背包)- 初始化:
f[0][0]=0
,其余是INF
,只能求价值最小值
- 初始化:
思路:
- 有问题的状态表示:
f[i,j,k]
:所有从前i
个物品中选,且氧气含量恰好是j
,氮气含量恰好是k
的所有选法的气缸重量总和的最小值
- 正确的状态表示:
f[i,j,k]
:所有从前i
个物品中选,且氧气含量至少是j
,氮气含量至少是k
的所有选法的气缸重量总和的最小值
- 状态计算:
- 不选第
i
个物品:f[i-1,j,k]
- 选第
i
个物品:f[i-1,j-vi,k-mi]+wi
- 初始化:
f[0,0,0] = 0
,f[0,j,k]=INF
初始一个都不选,那么氧气含量和氮气含量就是 0,且总重量也是 0。其它由于需要求最小值,所有全部初始化为INF
- 以上两个状态转移和初始化方式,适用于上面两个状态表示…那么肯定是有问题的
- 在有问题的状态表示中,状态转移时,
f[i-1,j-vi,k-mi]+wi
需要保证j>=vi,k>=mi
,因为不能为负数。体积恰好是负数时的状态是不成立的 - 在正确的状态表示中,状态表示的是体积至少是
j
,那针对于当前物品的体积大于当前枚举的j
的时候,该物品仍是可以使用的。例如,当前状态f[2][3]
代表氧气含量至少是 2, 氮气含量至少是 3 的最小气缸重量,同时满足这两个条件才能发生状态转移。若现在有一个物品,氧气含量为 3,氮气含量为 2,那么它至少需要 3 的氧气容量, 2 个氮气容量,它也是一种合法状态,只不过现在是多占了一个氧气体积没有使用罢了,但是它在该状态定义下确确实实是一种合法状态。所以,如果用该物品的话,状态转移方程为:f[0][1]+w
,表示氧气不再需要,再填补至少一个单位的氮气进来就行了。这样就能完美的进行状态转移。故,当氧气或者氮气枚举其体积一方小于 0 的时候,将该状态转移到 0 上去就行了。指其已经不需要再添进来该种气体了 - 状态转移方程:
f[i,j,k]=min(f[i-1,j,k],f[i-1,max(0,j-vi),max(0,k-mi)]
- 优化到二维:
f[j][k] = min(f[j][k], f[max(0, j - a)][max(0, k - b)] + w);
- 不选第
- 答案:
- 氧气含量至少为
n
氮气含量至少为m
的最小气缸重量f[n][m]
- 氧气含量至少为
枚举体积的三种方式,注意细节和初始化方式。
这边大佬题解可参考:
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005, M = 505, K = 105;
int n, m, t;
int f[N][M];
int main() {
cin >> n >> m >> t;
for (int i = 0; i < t; ++i) {
int a, b;
cin >> a >> b;
for (int j = n; j >= a; --j)
for (int k = m - 1; k >= b; --k)
f[j][k] = max(f[j][k], f[j - a][k - b] + 1);
}
cout << f[n][m - 1] << ' ';
int k = m - 1;
while (k > 0 && f[n][k - 1] == f[n][m - 1]) k --;
cout << m - k << endl;
return 0;
}