最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
c语言背包算法怎么实现
时间:2026-09-09 20:08:50 编辑:袖梨 来源:一聚教程网
在前端开发内容学习中,c语言背包算法怎么实现?0/1背包思路与代码示例是常见主题。很多人在阅读时会遇到概念分散、步骤不清和注意点难以归纳的问题。本文按照基础概念、操作流程和关键细节,对相关内容进行整理。

很多人搜c语言背包算法,真正想解决的通常不是名词解释,而是如何把问题拆成状态、写出转移并在C语言里落地。本文围绕0/1背包讲清建模思路、代码结构和常见易错点。
先弄清背包问题在求什么
背包问题的核心是,在总容量有限的条件下,从若干物品里选择一部分,让总价值尽量大。每件物品通常有两个属性:重量和价值,算法要回答的是怎样选最划算。
如果题目说明每件物品只能取一次,通常就是0/1背包;如果同一种物品可以重复取,通常就是完全背包;如果每种物品只能取有限件数,通常就是多重背包。本文下面给出的状态设计、代码模板、输入输出示例,都是围绕0/1背包展开的。
写C语言程序前,先确认题型,否则循环方向一错,结果就会直接失真。0/1背包一般是容量倒序,完全背包一般是容量正序;多重背包则常见做法是拆分成多个0/1物品,或者再配合其他优化方法处理。
- 这部分内容的完整代码示例对应的是0/1背包,不是所有背包题都能原样直接套。
- 如果题目允许同一物品重复选,核心区别通常不在公式本身,而在容量循环方向。
- 如果题目限制每种物品件数,先看件数和数据范围,再决定是否做二进制拆分。
状态设计和转移怎么写
0/1背包最常见的设计是用dp[j]表示容量恰好或不超过j时能够得到的最大价值。这样做的好处是数组结构简单,C语言实现直接,空间也比二维写法更省。
转移时要枚举每件物品,再判断当前容量j能不能放下它。若能放下,就比较"不选当前物品"和"选当前物品"两种结果,取其中较大值。
一维优化时,容量必须从大到小循环。因为每件物品只能用一次,倒序才能保证本轮更新时读取到的是上一轮物品留下的旧状态,而不是已经被当前物品污染过的新状态。比如处理第i件物品时,dp[j - w[i]]必须表示"前i-1件物品在容量j-w[i]时的最优值",这样加上v[i]才是合法的"选第i件"方案。
- 状态定义:dp[j]表示容量为j时的最大总价值。
- 初始条件:dp数组先全部置为0,表示什么都不选时价值为0。
- 转移公式:dp[j] =
max(dp[j], dp[j - w[i]] + v[i])。 - 循环顺序:物品在外层,容量j从bag逆序到w[i]。
- 如果把0/1背包写成容量正序,当前物品会在同一轮被重复利用,结果就偏成完全背包。
C语言完整示例
下面这份代码演示的是标准0/1背包。输入部分给出物品数量、背包容量、每件物品的重量和价值,程序输出最大总价值,适合作为练习和改写模板。
先看这份模板的适用前提:代码里写了MAX_N 105和MAX_W 1005,所以要求n不能超过MAX_N - 1,也就是104;bag不能超过MAX_W - 1,也就是1004。如果题目数据更大,要么把数组上限开大,要么改成动态分配,不能直接照抄。
输入格式也要对齐:第一行输入n和bag;接下来n行,每行输入两个整数,分别表示第i件物品的重量w[i]和价值v[i]。如果你的题目不是从标准输入读取,也可以保留核心循环不动,只替换数据来源。
- 输入格式:第一行是物品数量n和背包容量bag。
- 接下来n行:每行两个整数,依次是重量和价值。
- 适用范围:n <= 104,bag <= 1004。超出这个范围时,这份固定数组模板不能直接使用。
完整示例
#include <stdio.h> #define MAX_N 105 #define MAX_W 1005 int max(int a, int b) { return a > b ? a : b; } int main(void) { int n, bag; int w[MAX_N], v[MAX_N]; int dp[MAX_W] = {0}; if (scanf("%d %d", &n, &bag) != 2) { return 0; } for (int i = 1; i <= n; i++) { scanf("%d %d", &w[i], &v[i]); } for (int i = 1; i <= n; i++) { for (int j = bag; j >= w[i]; j--) { dp[j] = max(dp[j], dp[j - w[i]] + v[i]); } } printf("%dn", dp[bag]); return 0; }- 编译命令:
cc -std=c11 knapsack.c -o knapsack - 运行命令:
./knapsack < input.txt
这段代码怎么输入和怎么看输出
如果你想直接把代码跑起来,可以先准备一组最小可验证样例。下面这个例子里,一共有3件物品,背包容量是4。
第1件重量1、价值15;第2件重量3、价值20;第3件重量4、价值30。因为每件物品只能选一次,所以最优方案是选第1件和第2件,总重量4,总价值35。程序输出35,就说明这组数据跑通了。
示例输入
3 4 1 15 3 20 4 30对应输出
35- 如果你使用命令./knapsack < input.txt,那么input.txt里的内容就按上面的格式填写。
- 如果你直接运行./knapsack,程序会等待你在终端手动输入这些数字,输完后再回车结束。
关键循环怎么对应题意
很多人会背公式,但一进代码就不知道双重循环到底在干什么。其实可以把外层和内层直接翻译成题目动作。
外层第i轮,表示"现在考虑第i件物品,要不要把它放进背包"。内层从bag倒着枚举到w[i],表示"尝试把这件物品放到每个放得下它的容量里"。
还是以上面的样例说明。开始时dp全是0。处理第1件物品(重量1,价值15)后,容量1到4的位置都可以得到15。
处理第2件物品(重量3,价值20)时,先看j=4,dp[4]会比较原来的15和dp[1]+20,也就是35,所以更新成35;接着j=3,dp[3]会从15更新到20。到了第3件物品(重量4,价值30)时,j=4会比较35和dp[0]+30,最终仍保留35。
这就是倒序更新的意义:当第2件物品更新dp[4]时,用到的dp[1]还是上一轮第1件物品留下的值15,而不是本轮刚被第2件物品改写的新值。这样才能保证第2件物品只被算一次。
- 外层循环控制"当前处理哪一件物品"。
- 内层倒序循环控制"这件物品是否放入不同容量的背包"。
- 比较dp[j]和dp[j - w[i]] + v[i],本质上就是比较"不选它"和"选它"。
- 倒序的目标不是写法好看,而是防止同一件物品在同一轮被重复使用。
把模板套到题目里的实用步骤
实际做题时,不要一上来就抄代码。先用固定步骤把题意翻译成模板字段,能大幅减少出错。
最常见的落地方式,是先判断题目是不是"每件物品最多选一次、求最大价值",如果是,就把数据填进w数组、v数组和bag,再直接使用这份核心循环。若题目只改了输入来源、增加了多组测试,或者要求输出是否能装满,本质上都是在模板外围做小改动。
- 先看题目是否明确说明每件物品只能选一次;如果不是,就别直接套这份0/1背包模板。
- 从题目里提取三个量:物品数量n、背包容量bag、每件物品的重量和价值。
- 先用样例手算一个答案,再喂给程序,确认输出一致后再提交。
- 如果题目数据范围更大,优先检查数组上限是否够用,再决定是否改成更大的数组或动态分配。
- 如果题目要输出选了哪些物品,需要在这个价值模板基础上继续记录路径,而不是改动转移方向。
容易出错的地方和检查方法
背包题最常见的错误不是公式不会写,而是细节没对齐。比如把0/1背包写成顺序枚举容量,程序能编译也能输出,但答案会悄悄变成"可重复选取"的效果。
另一个高频问题是数组范围不足。背包容量上限如果比数组开得更大,轻则结果异常,重则直接越界。写C语言时要先看题目数据范围,再决定数组大小,不要拿固定模板直接套。
调试时可以先用很小的数据手算答案,再对照程序输出。只要两三个样例都能对上,状态定义、转移公式和循环方向通常就已经基本正确。
- 可以先测只有1件物品的情况,检查基础转移是否正确。
- 可以测背包容量小于所有物品重量的情况,结果应为0。
- 可以测两件物品取舍冲突的情况,确认程序确实在比较最优价值。
- 可以专门测bag或n接近数组上限的情况,确认模板没有越界风险。
c语言背包算法并不难,关键是先分清题型,再把状态、转移和循环方向对应好。本文给出的完整代码和样例主要解决的是0/1背包落地问题;把这个模板吃透后,再去看完全背包和多重背包,会更容易建立完整思路。