背包问题c++实现怎么写

作者:袖梨 2026-09-07

在前端开发内容学习中,背包问题c++实现怎么写?01背包与完全背包示例是常见主题。很多人在阅读时会遇到概念分散、步骤不清和注意点难以归纳的问题。本文按照基础概念、操作流程和关键细节,对相关内容进行整理。

img_6a9e55a5b763030.webp

背包问题是动态规划里的经典题型,很多人卡在状态定义、转移方向和代码细节上。本文围绕背包问题c++实现展开,分别说明01背包与完全背包的写法、核心区别、示例代码和自检方法,方便直接上手。

一、先弄清背包问题的核心模型

背包问题通常给定容量上限,以及若干物品的体积和价值,目标是在不超过容量的前提下,让总价值尽量大。写代码前,先分清每件物品能选几次,这是后续转移方向是否正确的关键。

最常见的是01背包和完全背包。01背包里每件物品最多选一次,完全背包里每件物品可以重复选择。两者状态定义往往相同,但一维优化时循环方向不同,很多实现错误都出在这里。

二、01背包的C++实现思路

01背包适合用一维动态规划优化。设dp[j]表示容量恰好不超过j时能得到的最大价值,处理每件物品时,从大到小枚举容量,避免同一轮里重复使用当前物品。

如果题目还要求输出选择方案,通常需要额外记录路径或回退信息。只是求最大价值时,一维数组已经足够,代码更短,也更适合竞赛和笔试场景。

状态转移公式

  • 01背包完整示例

    #include <algorithm>
    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int n = 4;
        int W = 5;
        vector<int> weight = {1, 2, 3, 4};
        vector<int> value = {15, 20, 30, 40};
        vector<int> dp(W + 1, 0);
    
        for (int i = 0; i < n; ++i) {
            for (int j = W; j >= weight[i]; --j) {
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
    
        cout << dp[W] << endl;
        return 0;
    }
  • 编译命令:g++ -std=c++17 knapsack01.cpp -o knapsack01
  • 运行命令:./knapsack01

样例结果怎么验证

三、完全背包的实现区别在哪里

完全背包的状态定义可以继续使用dp[j],区别在于每件物品允许重复选择,所以处理当前物品时,要从小到大枚举容量。这样当前轮更新过的dp[j - weight[i]]才能继续服务于同一件物品的再次选择。

如果把完全背包也写成从大到小循环,结果往往会退化成01背包。遇到题目描述里出现“每种物品可无限次使用”“硬币数量不限”“材料可重复拿取”等字样时,就要优先考虑完全背包。

上面的01背包示例运行后,预期输出是50。原因是容量为5时,最优选法是体积2、价值20和体积3、价值30这两件物品组合,总代价正好为5,总价值也是50。虽然体积1和体积4的组合也能装满,但价值只有15+40=55?这里要注意重新核对数据,实际上体积1价值15与体积4价值40相加等于55,所以真正最优答案应为55,程序输出也应该是55。

这个过程正好说明,手算一遍样例能及时发现自己理解是否有误。

你还可以顺手看一下dp数组的变化:处理完体积1的物品后,dp[1]到dp[5]都会先变成15;加入体积2价值20的物品后,dp[3]会更新到35,dp[5]会更新到35;再加入体积3价值30和体积4价值40后,dp[5]最终更新为55。只要程序没有输出55,就说明循环方向、下标或转移写错了。

完整代码示例

  • 完全背包完整示例

    #include <algorithm>
    #include <iostream>
    #include <vector>
    using namespace std;
    
    int main() {
        int n = 3;
        int W = 5;
        vector<int> weight = {1, 2, 3};
        vector<int> value = {10, 15, 40};
        vector<int> dp(W + 1, 0);
    
        for (int i = 0; i < n; ++i) {
            for (int j = weight[i]; j <= W; ++j) {
                dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
            }
        }
    
        cout << dp[W] << endl;
        return 0;
    }
  • 编译命令:g++ -std=c++17 complete_knapsack.cpp -o complete_knapsack

四、写背包代码时最容易错的地方

第一类错误是数组含义没统一,比如一开始把dp[j]当成恰好装满,后面又按不超过容量来转移,最后答案就会混乱。写之前先确定定义,再决定初始值是否需要负无穷或零。

第二类错误是循环方向写反。01背包必须倒序,完全背包通常正序。第三类错误是下标越界,尤其在j小于当前物品体积时仍然访问dp[j - weight[i]]。这些问题都可以通过手算小样例快速发现。

完全背包这组示例运行后,预期输出是60。因为容量为5时,可以选择体积2价值15的物品一次,再选择体积3价值40的物品一次,总体积正好是5,总价值达到55;但这还不是最优。由于物品可以重复拿,体积1价值10的物品可以反复使用,不过连续拿5次总价值只有50。

继续比较后会发现,最优方案其实是体积1价值10拿两次,再加体积3价值40一次,总体积5,总价值60,所以程序应该输出60。

如果你想继续手算,可以按物品顺序观察更新过程:先处理体积1时,dp[1]到dp[5]依次变成10、20、30、40、50;再处理体积2价值15时,部分状态不会超过原结果;处理体积3价值40时,dp[3]会更新成40,dp[4]会更新成50,dp[5]会更新成60。这样对照输出值,就能快速判断完全背包的正序循环是否写对。

  • 先用2到3个物品、容量很小的样例手动推一遍dp数组。
  • 检查每种物品是只能选一次,还是可以重复选取。
  • 确认最终输出的是dp[W],还是容量不超过W时的全局最优定义。
  • 示例里补上了#include <algorithm>,因为max通常来自这个头文件,复制代码时不要漏掉。

五、如何把模板真正用到题目里

实际做题时,不要急着套代码,先看题目是在求最大价值、方案数,还是恰好装满的最优值。目标不同,状态和初始化也会跟着变化,直接照搬模板很容易漏条件。

更稳妥的做法是先写出状态含义,再根据“可选次数”和“优化目标”决定转移。把01背包和完全背包的基础模板掌握后,再扩展到多重背包、分组背包,会顺畅很多。

背包问题c++实现并不难,难点主要在于分清题型和写对转移方向。把状态定义、循环顺序和小样例自检这三步固定下来,常见背包题基本都能稳定处理。

相关文章

精彩推荐