( 动态规划专题 )【 补充--记录路径的01背包 】
在01背包问题中,求出最优解并记录背包内物品,动态规划的方法求其问题,最核心的公式为f[i][j]=max{f[i-1][j],f[i-1][j-weight[i]]+value[i]},
在考虑当前第i个物品是否放入的时候就是比较
前面的i-1个物品放在容量为j的背包中时背包中总价值与
前面的i-1个物品放在容量为j-weight[i]的背包中并加上当前第i个的价值value[i]的总价值
比较完后选择当前i个物品放在j容量的包中的最佳方案。
同时在记录其路径的时候,路径为conf[i][j]=0或1,若当前f[i][j]为放入第i件物品,则为1,最终形成一个i行j列的矩阵
f形成的矩阵中第N行J列即为前N件物品放在J容量的包中最大价值解。
其路径便从第N行开始输出,路径conf形成的矩阵第N行J列即为第N件物品在包容量为J时是否放入,若N行J列为0,则代表第N件物品未放入J容量的包中,则再看第N-1件物品,若N-1件物品放入了包中即当前位置为1,则需要在包容量J上减去此时的weight[i],如此反复直到物品全部检索完或者容量小于0。
---------------------------HOXJUN
例题:E - Optimal Slots Gym - 102219E
Input
该行以两个整数sum和n开头,这两个整数是在特定周末分配给大厅使用的时间以及事件的数量。之后n个整数是事件的持续时间
5 5 1 2 3 4 5
10 9 11 9 3 5 8 4 9 3 2
16 8 12 6 11 11 13 1 10 7
13 5 10 12 2 13 10
28 14 18 19 26 15 18 24 7 21 14 25 2 12 9 6
0
Output
对于每行输入值,首先在一行中输出一个整数列表,这些整数是所选事件持续时间,另一个整数是所选事件持续时间的总和。
1 4 5
3 5 2 10
6 10 16
13 13
19 7 2 28
题意:给定sum,从n个数里挑选任意个数,使得挑选出的数的和小于sum并且最接近sum。输出最优的选择,如果和相等,优先选择更靠前的数。
思路:01背包+记录路径。这里要求选择更靠前的数,所以直接把a数组逆置。
代码:
#include <bits/stdc++.h>
using namespace std;
int a[2005];
int dp[2005];
int path[2005][2005];
int main()
{
int sum,n;
while( cin>>sum ) {
if ( sum==0 ) break;
cin >> n;
memset(path,0,sizeof(path));
for ( int i=n; i>=1; i-- ) scanf("%d",&a[i]);
memset(dp,0,sizeof(dp));
for ( int i=1; i<=n; i++ ) {
for ( int j=sum; j>=0; j-- ) {
if ( j>=a[i] ) {
if ( dp[j] <= dp[j-a[i]]+a[i] ) {
dp[j] = dp[j-a[i]]+a[i];
path[i][j] = 1; //若决定放入当前物品,则记为1
}
}
}
}
for ( int i=n,j=sum; i>0&&j>0; i-- ) { //路径记录
if ( path[i][j] ) { //第i件物品是否放入容量剩余j的包中
cout << a[i] << " ";
j -= a[i]; //减去当前已经放入的物品占值后的剩余包容量
}
}
cout << dp[sum] << endl;
}
return 0;
}