疯狂的背包问题(2) - 01背包问题(可行性问题)

使用01背包DP判断容量V是否可达,dp[c]记录容量c能否被某组物品恰好凑出,容量倒序枚举。

OJ: luogu

题目 ID: U663295

难度:普及-

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

日期: 2026-08-08 23:11

题意

N 件物品,每件一个体积 v_i,每件只能选一次。问是否存在一种选法,使所选物品的总体积恰好等于背包容量 V。

思路

一句话本质:判断是否存在一种选法让总体积恰好等于 V,不关心价值,只关心"能不能凑出来"。

brute.py 的做法和瓶颈是什么?

brute.py 枚举 2^N 种选法,对每种选法计算总体积,遇到等于 V 的就输出 true 并退出。N 最大 1000,2^1000 不可能跑完——如果没有符合的方案就需要全部枚举完才能判定 false。

这道题和最大价值版的 01 背包有什么不同?

最大价值版关心的是"在容量不超过 V 的前提下,最大价值是多少"。这里不涉及价值,只问一个布尔问题:容量 V 能不能被恰好凑出来。

容量 c 是否"可达",取决于什么?

如果存在某个物品体积为 v,且容量 c-v 是可达的,那么选上这个物品后容量 c 也是可达的。换句话说,c 的可达性可以从更小容量的可达性推导出来。

状态定义和转移是什么?

定义 dp[c] 为布尔值,表示容量 c 能否由某些物品恰好凑出。

初始 dp[0] = true(不选任何物品,总体积为 0,当然可达)。

对于每件物品体积 v,容量 c 从 V 到 v 倒序:

text
dp[c] = dp[c] || dp[c - v]

含义:要么原本就能凑出 c,要么能凑出 c-v 再加上当前物品就能凑出 c。

为什么容量倒序?

和最大价值版一样,每件物品只能选一次。倒序保证 dp[c - v] 还没有被当前物品影响,避免同一件物品被用多次。

代码

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-08-08 23:11
 * update_at: 2026-08-08 23:11
 */
#include <bits/stdc++.h>
using namespace std;

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int N, V;
    cin >> N >> V;
    vector<bool> dp(V + 1);
    dp[0] = true;
    for (int i = 0; i < N; ++i) {
        int v;
        cin >> v;
        for (int c = V; c >= v; --c)
            dp[c] = dp[c] || dp[c - v];
    }
    cout << (dp[V] ? "true" : "false") << '\n';
    return 0;
}

复杂度

  • 时间:O(N × V),每件物品遍历 V 个容量
  • 空间:O(V),dp 数组大小 V+1

总结

可行性问题把 dp 值从"最优价值"简化为"布尔可达性"。核心逻辑不变——每件物品只能选一次,容量倒序枚举。dp[0] = true 是基石,其他容量通过 dp[c] |= dp[c - v] 层层推导。