本文讨论在同时受限于体积和重量条件下的0-1背包问题。每个物品具有特定的体积、重量和价值,目标是在不超过容器最大体积和最大承重的前提下,选择若干物品使总价值最大化。
此类问题可视为三维动态规划模型,通过状态压缩优化为二维处理方式。定义状态数组 `dp[vol][wt]` 表示当可用体积为 `vol`、可用重量为 `wt` 时所能获得的最大价值。
由于是0-1背包问题(每件物品最多选一次),需对体积和重量维度均采用逆序遍历,以避免物品被重复选取。
核心状态转移方程如下:
dp[vol][wt] = max(dp[vol][wt],
dp[vol - volume[i]][wt - weight[i]] + value[i])
其中 `i` 表示当前考虑的物品索引。
下面是基础实现代码,仅计算最大价值:
#include <bits/stdc++.h>
using namespace std;
const int MAX_SIZE = 1006;
int dp[MAX_SIZE][MAX_SIZE]; // dp[vol][wt]:指定体积和重量限制下的最大价值
int volume[MAX_SIZE], weight[MAX_SIZE], value[MAX_SIZE];
int main() {
int n, maxVol, maxWt;
cin >> n >> maxVol >> maxWt;
for (int i = 0; i < n; ++i) {
cin >> volume[i] >> weight[i] >> value[i];
}
// 动态规划填表
for (int i = 0; i < n; ++i) {
for (int vol = maxVol; vol >= volume[i]; --vol) {
for (int wt = maxWt; wt >= weight[i]; --wt) {
dp[vol][wt] = max(dp[vol][wt],
dp[vol - volume[i]][wt - weight[i]] + value[i]);
}
}
}
cout << dp[maxVol][maxWt] << endl;
return 0;
}
若还需输出具体选择了哪些物品,则需要引入路径追踪机制。为此,使用两个辅助数组:
- `chosenFrom[vol][wt]`:记录该状态是由哪一个物品更新而来(存储物品索引)。
- `prevState[vol][wt]`:记录转移前的状态坐标,即前一体积与重量组合 `(prev_vol, prev_wt)`。
初始化所有状态为未更新状态(用 `-1` 表示)。在状态转移过程中,一旦发现更优解,则更新这两个数组。
完整路径还原版本如下:
#include <bits/stdc++.h>
using namespace std;
const int MAX_SIZE = 1006;
int dp[MAX_SIZE][MAX_SIZE];
int chosenFrom[MAX_SIZE][MAX_SIZE];
pair<int, int> prevState[MAX_SIZE][MAX_SIZE];
int volume[MAX_SIZE], weight[MAX_SIZE], value[MAX_SIZE];
int main() {
int n, maxVol, maxWt;
cin >> n >> maxVol >> maxWt;
for (int i = 0; i < n; ++i) {
cin >> volume[i] >> weight[i] >> value[i];
}
memset(chosenFrom, -1, sizeof(chosenFrom));
// 填充DP表并记录路径
for (int i = 0; i < n; ++i) {
for (int vol = maxVol; vol >= volume[i]; --vol) {
for (int wt = maxWt; wt >= weight[i]; --wt) {
int candidate = dp[vol - volume[i]][wt - weight[i]] + value[i];
if (candidate > dp[vol][wt]) {
dp[vol][wt] = candidate;
chosenFrom[vol][wt] = i;
prevState[vol][wt] = make_pair(vol - volume[i], wt - weight[i]);
}
}
}
}
cout << "最大价值:" << dp[maxVol][maxWt] << endl;
// 回溯构造选取路径
vector<int> selectedItems;
int curVol = maxVol, curWt = maxWt;
while (chosenFrom[curVol][curWt] != -1) {
int itemIdx = chosenFrom[curVol][curWt];
selectedItems.push_back(itemIdx);
tie(curVol, curWt) = prevState[curVol][curWt]; // 解包上一状态
}
reverse(selectedItems.begin(), selectedItems.end());
cout << "选中的物品编号(从0起始):" << endl;
for (int idx : selectedItems) {
cout << idx << " ";
}
cout << endl;
return 0;
}
关键点说明:`tie(curVol, curWt) = prevState[curVol][curWt];` 使用 C++ 的 `std::tie` 将 `pair` 类型中的两个元素分别赋值给变量,实现状态回退。
最终程序输出最大价值以及被选中的物品序列。