A 装箱问题
- 从 n 件物品中选取若干件装入箱子,求箱子最后剩余空间的最小值,即求箱子内物品总体积的最大值。
- 本题为 01 背包问题,将物品的体积同时看作价值。用 dp[i][v] 表示前 i 件物品恰好装入容量为 v 的箱子时物品总体积的最大值。在求解 dp[i][v] 时,考虑对第 i 件物品的选择策略:①不放第 i 件物品,那么问题转化为前 i-1 件物品恰好装入容量为 v 的箱子所能获得的最大体积;②放第 i 件物品,那么问题转化为前 i-1 件物品恰好装入容量为 v-v[i] 的箱子所能获得的最大体积。故状态转移方程为边界条件为:,最后枚举 i 在1~n 范围内的 dp[i][V],取其最大值max,V-max 即为箱子最后剩余空间的最小值。时间复杂度和空间复杂度均为 。
- 考虑对空间进行优化,使用一维数组,此时对 v 的枚举必须为逆序。参考代码如下。
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 20010;
int dp[MAXN], w[50];
int V, n;
int main() {
cin >> V >> n;
for (int i = 1; i <= n; i++)
cin >> w[i];
fill(dp, dp + MAXN, 0);
for (int i = 1; i <= n; i++)
for (int v = V; v >= w[i]; v--)
dp[v] = max(dp[v], dp[v - w[i]] + w[i]);
cout << V - dp[V] << endl;
return 0;
}
B 采药
- 本题即为标准的 01 背包问题。参考代码如下。
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXT = 1010;
const int MAXM = 110;
int T, M;
int dp[MAXT], w[MAXM], c[MAXM];
int main() {
cin >> T >> M;
for (int i = 1; i <= M; i++)
cin >> w[i] >> c[i];
fill(dp, dp + MAXT, 0);
for (int i = 1; i <= M; i++)
for (int v = T; v >= w[i]; v--)
dp[v] = max(dp[v], dp[v - w[i]] + c[i]);
cout << dp[T] << endl;
return 0;
}
C 货币系统
- 本题为完全背包问题,但是和原始的背包问题不同,这里是求方案数。用 dp[i][v] 表示用前 i 种货币能表示 v 数量钱的方案数,则对第 i 种货币有选和不选的策略,故总的方案数为二者之和。因此可得状态转移方程为,边界条件为。同样也可以改写成一维形式,但是必须正向枚举,此时状态转移方程为 ,边界条件为。参考代码如下。
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 10010;
int V, N, w[30];
long long dp[MAXN];
int main() {
while (cin >> V >> N) {
for (int i = 1; i <= V; i++)
cin >> w[i];
fill(dp, dp + MAXN, 0);
dp[0] = 1; //当 v - w[i] == 0 时,是一种货币选择方案,故此边界条件为 1
for (int i = 1; i <= V; i++)
for (int v = w[i]; v <= N; v++)
dp[v] = dp[v] + dp[v - w[i]];
cout << dp[N] << endl;
}
return 0;
}