【洛谷 P1049】[NOIP2001 普及组] 装箱问题 题解(动态规划+01背包)

108 阅读2分钟

[NOIP2001 普及组] 装箱问题

题目描述

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

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

输入格式

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

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

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

输出格式

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

样例 #1

样例输入 #1

24
6
8
3
12
7
9
7

样例输出 #1

0

提示

对于 100%100\% 数据,满足 0<n300<n \le 301V200001 \le V \le 20000

【题目来源】

NOIP 2001 普及组第四题


思路

首先从输入中读取箱子的总容量v和物品的数量n,然后读取每个物品的体积并存储在数组a中。

一维数组 dp[j] 用于存储当箱子的容量为j时,能够装入的最大体积。也就是说,对于一个给定的jdp[j]表示的是在不超过j容量限制的前提下,能够装入的物品的最大总体积。

状态转移方程:

dp[j] = max(dp[j], dp[j - a[i]] + a[i])

更新当前容量j下,能够装入的最大体积。

这个操作是在比较两种情况:一种是不放入当前物品a[i]时的最大体积dp[j],另一种是放入当前物品a[i]后的总体积dp[j - a[i]] + a[i]。取这两种情况的最大值,就得到了新的dp[j],也就是在考虑了当前物品a[i]之后,能够装入的最大体积。

动态规划的过程在第二个for循环中。这个循环遍历每个物品,然后对于每个物品,从箱子的总容量开始递减,直到当前物品的体积。这个循环的目的是尝试把当前物品放入箱子,并更新箱子的当前容量。如果把当前物品放入箱子后,箱子的容量能够增大(即更接近满载),那么就更新箱子的当前容量。这个动态规划的过程实际上是在尝试所有可能的组合,找到能够最大化箱子容量的物品组合。

最后,输出v - dp[v],这是箱子未被装满的体积,也就是箱子的剩余空间。


AC代码

#include <algorithm>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;

const int N = 1e7 + 7;

int v, n;
int a[N];
int dp[N];

int main() {
	cin >> v >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		for (int j = v; j >= a[i]; j--) {
			dp[j] = max(dp[j], dp[j - a[i]] + a[i]);
		}
	}
	cout << v - dp[v] << endl;
	return 0;
}