洛谷P1049 装箱问题(01背包问题应用)

题目描述

有一个箱子容量为 V V V,同时有 n n n 个物品,每个物品有一个体积。

现在从 n n n 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。

输入格式

第一行共一个整数 V V V,表示箱子容量。

第二行共一个整数 n n n,表示物品总数。

接下来 n n n 行,每行有一个正整数,表示第 i i i 个物品的体积。

输出格式

共一行一个整数,表示箱子最小剩余空间。

样例输入

24
6
8
3
12
7
9
7

样例输出

0

提示

对于 100 % 100\% 100% 数据,满足 0 < n ≤ 30 00<n30 1 ≤ V ≤ 20000 1 \le V \le 20000 1V20000

分析题目:题目就是将背包问题中的包换成了箱子,让求最小剩余体积,也就是所能装的最大体积,思路还是不变,每个物品就两种选择,拿或者不拿。

状态表示:f[i] 表示容量为i的包所装物品的最大体积。
状态计算:
*************拿:f[i] = f[i]
*************不拿:f[i] = f[i - v[i]] + v[i]
******二者取最大值

  • 完整代码
    看了很多题解,关于为什么体积要从大到小进行枚举貌似很少有人提。

如果从小到大进行枚举,那么就会重复拿物品。因为我们每个体积的初始化都是0,我们想要拿取的条件是 f [ j ] < f [ j − v [ i ] ] + v [ i ] , f[j] < f[j - v[i]] + v[i], f[j]<f[jv[i]]+v[i],左边是0,右边肯定大于0,那么我们每次就是必拿,势必会造成重复拿的问题。
举个例子
输入:3 1 1
f[1]= f[0] + v[1] = 1
f[2] = f[2 - 1] + v[1] = 2
f[3] = f[3 - 1] + v[1] = 3
那么我们最后的结果就是V - 3 = 0,而正确结果应该是V - 1 = 2,这就是因为第一个物品被重复拿了
如果我们是从大到小进行枚举,在计算大体积时,就不会出现这种情况,只会拿一次
还拿上面那个例子说
f[3] = f[2] + v[1] = 0 + 1 = 1
f[2] = f[1] + v[1] = 0 + 1 = 1
f[1] = f[0] + v[1] = 0 + 1 = 1
答案就是正确的
因为我们在计算每一个体积时,不使用我们上面已经算出来的结果,每一个都是重新算,就不会重复拿了

#include 
#include 
#include 
#include 
#include 
using namespace std;

const int N = 20005;
int V, n, f[N], v[35];
int main() {
	cin >> V;
	cin >> n;
	for (int i = 1; i <= n; i ++) {
		cin >> v[i];
	}

	for (int i = 1; i <= n; i ++) {
		for (int j = V; j >= v[i]; j --) {
			if (f[j] < f[j - v[i]] + v[i]) {
				f[j] = f[j - v[i]] + v[i];
			}
		}
	}
	cout << V - f[V] << endl;
	return 0;
}

本题的分享就结束了,本题的细节就是在枚举的时候要从大到小进行枚举,也是难想到的一个地方。有问题的小伙伴可以在评论区留言
别忘了点赞关注加收藏!

你可能感兴趣的:(算法,数据结构,DP,背包问题)