C语言背包问题
背包问题是算法领域经典的动态规划入门问题,也是计算机考研、笔试面试的高频考点。它本质是一个资源优化问题,核心是在有限容量内,选择物品使得总价值最大。本文将带你深入理解背包问题的核心原理,用C语言实现最常见的0-1背包和完全背包,并详解代码逻辑与优化思路,零基础也能轻松看懂!
一、背包问题基础概念
背包问题主要分为三类,其中0-1背包是基础,完全背包是高频变体,多重背包是两者的延伸,我们先理清核心定义:
-
0-1背包:每个物品只有1件,只能选或不选,不可分割,是最基础的背包问题。
-
完全背包:每个物品有无限件,可重复选取,只要不超过背包容量。
-
多重背包:每个物品有固定数量件数,介于0-1背包和完全背包之间。
通用问题描述
给定一个容量为C的背包,有n个物品,每个物品的重量为w[i],价值为v[i]。
要求:在不超过背包容量的前提下,选出物品,使得总价值最大化。
二、0-1背包问题详解(C语言实现)
0-1背包是所有背包问题的基础,核心思想是动态规划,通过拆分子问题,逐步推导最优解。
- 动态规划原理
• 状态定义:定义二维数组dp[i][j],表示前i个物品,在背包容量为j时,能获得的最大价值。
• 状态转移方程:
-
若当前物品重量w[i] > 背包容量j:无法放入该物品,最大价值等于不放该物品的价值,即dp[i][j] = dp[i-1][j]。
-
若当前物品重量w[i] ≤ 背包容量j:选择放入或不放入,取价值更大的方案,即dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])。
• 边界条件:没有物品(i=0)或背包容量为0(j=0)时,最大价值都为0。
- 二维数组C语言实现
#include <stdio.h>
// 定义最大值,方便数组初始化
#define MAX 100
// 求两个数的最大值
int max(int a, int b) {
return a > b ? a : b;
}
int main() {
// 物品数量
int n;
// 背包容量
int capacity;
printf("请输入物品数量和背包容量:");
scanf("%d %d", &n, &capacity);
// 存储物品重量,下标从1开始
int w[MAX];
// 存储物品价值,下标从1开始
int v[MAX];
// dp[i][j]:前i个物品,容量j的最大价值
int dp[MAX][MAX] = {0};
// 输入每个物品的重量和价值
for (int i = <= n; i++) {
printf("请输入第%d个物品的重量和价值:", i);
scanf("%d %d", &w[i], &v[i]);
}
// 动态规划填充dp数组
for (int i = 1; i<= n; i++) {
for (int j<= capacity; j++) {
if (w[i] > j) {
// 物品重量大于容量,不放入
dp[i][j] = dp[i-1][j];
} else {
// 选择放入或不放入,取最大值
dp[i][j] = max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
}
}
}
// 输出最终最大价值
printf("背包能装的最大价值为:%d\n", dp[n][capacity]);
return 0;
}
三、完全背包问题详解(C语言实现)
完全背包和0-1背包的唯一区别:物品可重复选取,代码只需做一处修改!
- 核心区别
完全背包的状态转移,无需逆序遍历容量,正序遍历即可让物品重复放入背包。
状态转移方程:dp[j] = max(dp[j], dp[j - w[i]] + v[i])
- C语言完整代码
#include <stdio.h>
#define MAX 100
int max(int a, int b) {
return a > b ? a : b;
}
int main() {
int n, capacity;
printf("请输入物品数量和背包容量:");
scanf("%d %d", &n, &capacity);
int w[MAX], v[MAX];
int dp[MAX] = {0};
for (int i = 1; i<= n; i++) {
scanf("%d %d", &w[i], &v[i]);
}
// 正序遍历容量,允许重复选取物品
for (int i = 1;<= n; i++) {
for (int j = w[i]; j<= capacity; j++) {
dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
}
}
printf("背包能装的最大价值为:%d\n", dp[capacity]);
return 0;
}
四、0-1背包与完全背包核心区别
| 对比项 | 0-1背包 | 完全背包 |
|---|---|---|
| 物品数量 | 每个物品仅1件 | 每个物品无限件 |
| 容量遍历顺序 | 逆序遍历(从大到小) | 正序遍历(从小到大) |
| 核心逻辑 | 每个物品只选一次 | 物品可重复选择 |
| 空间复杂度 | O(C) | O(C) |
| 时间复杂度 | O(n*C) | O(n*C) |
五、背包问题拓展与易错点
-
易错点:0-1背包逆序遍历、完全背包正序遍历,千万不要搞反,否则会出现物品重复选取/无法选取的问题。
-
多重背包:可将有限数量的物品拆分为多个0-1背包物品,再用0-1背包代码实现。
-
适用场景:资源分配、预算规划、货物装载、项目投资等优化类问题。
六、总结
背包问题是动态规划的经典入门题,核心是状态定义+状态转移,掌握0-1背包的原理后,完全背包只需修改遍历顺序即可轻松实现。
先理解二维数组的动态规划逻辑,再过渡到一维数组优化,通过手动模拟dp数组填充过程,彻底吃透背包问题的核心思想。
浙公网安备 33010602011771号