如何输出最小背包?
0-1背包
#include <iostream>
using namespace std;
const int maxn=1005;
int dp[maxn][maxn];
int weight[maxn];
int value[maxn];
int n,v;
int max(int a,int b){
if(a>b) return a;
return b;
}
int main(int argc, char const *argv[])
{
int T;
cin>>T;
while(T--){
cin>>n>>v;
for(int i=1;i<=n;i++){
cin>>value[i];
}
for(int i=1;i<=n;i++){
cin>>weight[i];
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++){
for(int j=v;j>=weight[i];j--){
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
int out=dp[1][v];
for(int k=2;k<=n;k++)
out = max(out,dp[k][v]);
cout<<out<<endl;
}
/* code */
return 0;
}
其中i表示放第i个物品,j表示背包所容纳的重量,那么tab[i-1][j-weight[i]]+value[i]表示放入第i物品,刚开始接触会有疑问,tab[i-1][j-weight[i]]这个值,可以这样理解:tab[i-1][j]为装到上一个物品在背包j容量时的最佳值,那么如果我要求在j容量的时候放入现在的i物品的价值,那么是不是要先得到容量为(j-weight[i])时候的价值,即先得到 tab[i-1][j-weight[i]] ,所以
tab[i-1][j-weight[i]]+value[i] 为放入第i物品的价值; tab[i-1][j] 就是不放入第i个物品。
但是以上的做法是错误的
由于这样只更新了j>W[i]的情况,对于下一件物品,如果它的W[i+1]<[i],那么很可能这个W[i][j]为O,这就会导致错误
所以下标只能从J=O开始
for(int i=1;i<=n;i++){
for(int j=0;j>=weight[i];j--){
<span style="white-space:pre"> </span>if(j<w[i]) dp[i][j]=dp[i-1][j]
<span style="white-space:pre"> </span>else<span style="white-space:pre"> </span>
dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
}
}
优化空间复杂度以后
#include <iostream>
const int maxn=1005;
using namespace std;
int weight[maxn];
int value[maxn];
int dp[maxn];
int max(int a,int b){
return a>b?a:b;
}
int main(int argc, char const *argv[])
{
int T;
cin>>T;
int N,V;
while(T--){
cin>>N>>V;
for(int i=0;i<N;i++){
cin>>value[i];
}
for(int i=0;i<N;i++){
cin>>weight[i];
}
memset(dp,0,sizeof(dp));
for(int i=0;i<N;i++){
for(int j=V;j>=weight[i];j--){
dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
}
}
cout<<dp[V]<<endl;
}
/* code */
return 0;
}
为什么从后面到前面?因为后面到前面不会影响前面的状态。如果是从前到后,可能包含了一个物品取多个的情况。
事实上,从前往后正是完全背包的做法
明确状态转移方程的意义。状态转移在与是否取第M个物品,如果取第M个物品,那么这个位置就标为M,用一个chose数组记录是否选取M,然后通过回溯确定某个位置是否为1
#include <iostream>
using namespace std;
const int maxn=10005;
int dp[maxn];
int w[maxn];
int v[maxn];
bool chose[maxn][maxn];
int max(int a,int b){
return a>b?a:b;
}
int out[maxn];
int indexofout;
void print(int m,int v){
if(m==0) return ;
if(!chose[m][v])
print(m-1,v);
else {
out[indexofout++]=m;
print(m-1,v-w[m]);
}
}
int main(int argc, char const *argv[])
{
int N,V;
int T;
cin>>T;
while(T--){
cin>>N>>V;
indexofout = 0;
memset(dp,0,sizeof(dp));
memset(chose,false,sizeof(chose));
for(int i=1;i<=N;i++){
cin>>v[i];
}
for(int i=1;i<=N;i++){
cin>>w[i];
}
for(int i=1;i<=N;i++){
for(int j=V;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
if(dp[j]==dp[j-w[i]]+v[i]) chose[i][j]=true;
}
}
print(N,V);
for(int i=0;i<indexofout;i++){
cout<<out[i]<<" ";
}
cout<<endl;
cout<<dp[V]<<endl;
}
return 0;
}