装箱问题

GitHub跳转原题关系图返回列表

把装箱问题化成"体积=价值"的 0/1 背包:逐件物品倒序扫容量做一维 DP,dp[V] 即最大装载体积,答案为 V-dp[V]。

OJ: roj

题目 ID: 3507

难度:普及-

标签:动态规划背包01背包python

创建: 2026-10-02 04:05

更新: 2026-10-06 12:15

题目描述

容量为 V(0<V≤20000)的箱子,有 n(0<n≤30)件体积为正整数的物品,任取若干件装入箱内,使箱子剩余空间最小。 输入第一行 V,第二行 n,之后 n 行每行一件物品体积;输出一个整数,即最小剩余空间。 样例输入 24 / 6 / 8 / 3 / 12 / 7 / 9 / 7,输出 0。

思路

把"体积"同时当作"价值",问题就是 0/1 背包:最大化装入的总体积,答案即 V 减去该最大值。用一维 dp[c] 表示容量 c 能装下的最大体积,每件物品倒序扫容量做 dp[c]=max(dp[c],dp[c-w]+w),总时间 O(nV)。

参考代码

Python 版:

python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-02 04:05
# update_at: 2026-10-02 04:05

import sys


def knapsack(cap: int, vols: list[int]) -> list[int]:
    """0/1 背包:dp[c] = 只用已处理物品、容量 c 时能装下的最大总体积。"""
    dp = [0] * (cap + 1)
    for v in vols:
        # 倒序扫容量,保证每件物品只被选一次
        dp[v:] = map(max, dp[v:], (used + v for used in dp[:-v or None]))
    return dp


def solve() -> None:
    data = iter(map(int, sys.stdin.buffer.read().split()))
    box_volume = next(data)  # 箱子容量 V
    n = next(data)
    vols = [next(data) for _ in range(n)]

    best = knapsack(box_volume, vols)[box_volume]
    print(box_volume - best)  # 装得越多,剩余空间越小


if __name__ == "__main__":
    solve()

C++ 版(同一算法):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-10-06 12:15
 * update_at: 2026-10-06 12:15
 */

#include <iostream>
using namespace std;

typedef long long ll;

const int MAXV = 20005;

int dp[MAXV]; // dp[c]:只用已处理过的物品、容量 c 时能装下的最大总体积

int main() {
    int V; // 箱子容量
    int n; // 物品件数
    cin >> V >> n;

    // 0/1 背包:体积即价值,装入总体积越大,剩余空间越小
    for (int i = 1; i <= n; i++) {
        int w;
        cin >> w;
        // 倒序扫容量,保证每件物品最多被装入一次
        for (int c = V; c >= w; c--) {
            if (dp[c - w] + w > dp[c]) {
                dp[c] = dp[c - w] + w;
            }
        }
    }

    cout << V - dp[V] << endl; // 最小剩余空间
    return 0;
}