基于完全背包的装箱问题和01背包的装箱问题

给定一个箱子,以及一批货物,每个货物的体积已知,问是否能把该箱子恰好装满.注意,每个货物认为有无穷多个,可以多次使用,只要最终箱子填满即可

思路: 该题是一个很显然的完全背包问题,只不过与以往不同,以往的完全背包是既有代价w[i]又有价值v[i]的,求解最大价值,而此题则是没有价值,而是求解是否恰能装满,虽然问法不同,条件不同,但归根结底,还是一个完全背包问题,所以自然是要用完全背包去解决.

首先,标记dp[0]=true,表示代价(本题中视为体积)为0的箱子一定能够装满,这是显然的事情,然后基于此,如果某个容量的箱子减去某个w[i]刚好等于0的话,那么该商品就也能被装满,而同样的,如果之后又有一个箱子减去某个代价w[i]又恰好等于了0或者刚刚那种情况的时下标,就也能被装满,所以,我们得出如下代码

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
bool dp[200];
int w[200];
int main() {
	/*
	4 9
	2 4 5 6
	*/
	int n,v;
	cin>>n>>v;				//n是物品个数 v是背包总容量
	for(int i=1; i<=n; i++) {
		cin>>w[i];
	}
	dp[0]=true;
	for(int i=1; i<=n; i++) {
		for(int j=w[i]; j<=v; j++) {
			if(dp[j-w[i]]) {
				dp[j]=true;
			}
		}
		for(int i=0; i<=v; i++) {
			cout<<dp[i]<<' ';
			if(i==v)cout<<endl;
		}
	}
	cout<<(dp[v]==true?"true":"false");
	return 0;
}

该代码外层遍历每一个商品的下标,内层从w[i]开始到箱子的容量v去判断是否dp[j-w[i]]后等于true,如果等于,显然该商品就也能被装满,那么问题来了,为什么要从前往后推而不是从后往前推呢?那是因为该背包是个完全背包,每个物品可以随意次数的加,也就是说,虽然我的这个物品已经使用过,使用过之后该处的dp[j]就变成了true,但是我以后再碰到还可以再用一次这个物品,因为物品的数量是不受限制的,所以我们要从前往后遍历,对应的,如果题目要求是一个01背包,那么显然,每个物品只能用一次,那么我们就不能从前往后遍历了,相反的,应该从后往前去遍历,为什么呢,这就是跟最普通的01背包问题类似,普通的01背包问题一开始用二维数组,循环体内每次都是用到dp[i-1] [j],之后转成滚动数组后仍然是用上一层的数据,直到最后更换成一维数组版的背包问题时,才发现,如果从前往后遍历,物品会被多拿,但是从后往前遍历时,每次的j前面的dp[j-w[i]]都还没有被同层后面的j使用过,也就是说还没有被装入背包,这样的话,就不会出现重复;这道题的原理跟这个是完全一样的.

下面给出01背包装箱问题的题解:
题目与上题一样,唯一区别在于每个物品只能被装一次

#include<iostream>
#include<vector>
#include<algorithm>
#include<string>
using namespace std;
bool dp[200];
int w[200];
int main() {
	/*
	4 9
	2 4 1 6
	*/
	int n,v;
	cin>>n>>v;
	for(int i=1; i<=n; i++) {
		cin>>w[i];
	}
	dp[0]=true;
	for(int i=1; i<=n; i++) {
		for(int j=v; j>=w[i]; j--) {
			if(dp[j-w[i]]) {
				dp[j]=true;
			}
		}
		for(int i=0; i<=v; i++) {
			cout<<dp[i]<<' ';
			if(i==v)cout<<endl;
		}
	}
	cout<<(dp[v]==true?"true":"false");
	return 0;
}

发布了99 篇原创文章 · 获赞 13 · 访问量 2681

猜你喜欢

转载自blog.csdn.net/qq_44378358/article/details/103656057