0-1背包问题:输出最小背包

本文深入探讨了背包问题的解决方法,包括基本的0-1背包算法、完全背包算法和改进的空间复杂度优化,同时解释了状态转移方程的重要意义,并通过实例展示了从前往后的完全背包做法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何输出最小背包?

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;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值