1 01背包输出方案

2 按字典序顺序输出所选背包物品
举个例子,输入的数据↓
bash
N=5, V=5
1: v=2 w=3
2: v=1 w=0
3: v=1 w=0
4: v=2 w=3
5: v=1 w=5
按照逆向dp,构造出的dp数组(dpij表示体积为j时,放第i件物品的最大价值)。如下表所示,dp1V存储了n件物品,体积为V时的最大价值。搜索时,正序遍历dp数组,内层循环逆序遍历体积,如果dpi+1curV - v\[i] + wi == dpicurV),说明第i件物品有选。
| 体积/物品 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 物品1 | 0 | 5 | 5 | 8 | 8 | 11 |
| 物品2 | 0 | 5 | 5 | 8 | 8 | 8 |
| 物品3 | 0 | 5 | 5 | 8 | 8 | 8 |
| 物品4 | 0 | 5 | 5 | 8 | 8 | 8 |
| 物品5 | 0 | 5 | 5 | 5 | 5 | 5 |
参考代码
cpp
#include<bits/stdc++.h>
using namespace std;
int n, V;
int v[1005], w[1005];
int dp[1005][1005]; // dp[i][j]: 从 i 到 n,容量 j 的最大价值
bool chosen[1005];
int main(){
cin >> n >> V;
for(int i = 1; i <= n; i++){
cin >> v[i] >> w[i];
}
// 逆序 DP
for(int i = n; i >= 1; i--){
for(int j = 0; j <= V; j++){
dp[i][j] = dp[i+1][j];
if(j >= v[i]){
dp[i][j] = max(dp[i][j], dp[i+1][j - v[i]] + w[i]);
}
}
}
// 从前往后贪心,保证字典序最小
int curV = V;
for(int i = 1; i <= n; i++){
if(curV >= v[i] && dp[i+1][curV - v[i]] + w[i] == dp[i][curV]){
chosen[i] = true;
curV -= v[i];
}
}
for(int i = 1; i <= n; i++){
if(chosen[i]){
cout << i<<" ";
}
}
cout << "\n";
return 0;
}
例题2 洛谷P2066
对于编号小的尽量少分配机器,再做dp时考虑正向。搜索方案的时候逆向搜索。
参考代码
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m, a[20][20], dp[20][20];
int ansk[20];
signed main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
cin >> a[i][j];
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 1; j--) {
dp[i][j] = dp[i - 1][j];
for (int k = 1; k <= j; k++)
dp[i][j] = max(dp[i][j], dp[i - 1][j - k] + a[i][k]);
}
}
cout << dp[n][m] << endl;
int curM = m;
for (int i = n; i >= 1; i--) {
for (int k = curM; k >= 1; k--) {
if (dp[i][curM] == dp[i - 1][curM - k] + a[i][k]) {
ansk[i] = k;
curM -= k;
break;
}
}
}
for (int i = 1; i <= n; i++)
cout << i << " " << ansk[i] << endl;
return 0;
}