在算法世界中,动态规划(Dynamic Programming)是一种非常强大的思想,尤其适用于解决具有重叠子问题和最优子结构的问题。而背包问题正是动态规划最经典的入门案例之一。今天,我们将用 C# 语言,从零开始,手把手教你理解和优化 0-1 背包问题。
假设你有一个容量为 W 的背包,还有 n 件物品,每件物品有各自的重量(weight)和价值(value)。你的目标是:在不超过背包容量的前提下,选择若干物品装入背包,使得总价值最大。
注意:每件物品只能选一次或不选(这就是“0-1”的含义)。

最直观的想法是:枚举所有可能的物品组合(共 2^n 种),然后找出满足容量限制且价值最大的组合。但当 n 很大时(比如 n=30),这种方法就完全不可行了。
而动态规划通过记忆化避免重复计算,将时间复杂度从指数级降到多项式级别!
我们定义一个二维数组 dp[i][w],表示:
从前 i 件物品中选择,且背包容量为 w 时,能获得的最大价值。
状态转移方程如下:
if (当前物品重量 > 当前容量 w) { dp[i][w] = dp[i-1][w]; // 不能选,继承上一行} else { dp[i][w] = Math.Max( dp[i-1][w], // 不选第 i 件 dp[i-1][w - weight[i]] + value[i] // 选第 i 件 );}public static int Knapsack(int[] weights, int[] values, int capacity){ int n = weights.Length; int[,] dp = new int[n + 1, capacity + 1]; for (int i = 1; i <= n; i++) { for (int w = 0; w <= capacity; w++) { if (weights[i - 1] > w) { dp[i, w] = dp[i - 1, w]; } else { dp[i, w] = Math.Max( dp[i - 1, w], dp[i - 1, w - weights[i - 1]] + values[i - 1] ); } } } return dp[n, capacity];}这个实现清晰易懂,但空间复杂度是 O(n × W),当容量 W 很大时(比如 10^6),内存可能不够用。
观察发现:计算 dp[i][w] 时,只依赖于上一行 dp[i-1][...] 的数据。因此,我们可以用一维数组来代替二维数组,从而将空间复杂度优化为 O(W)。
关键点:**内层循环必须从后往前遍历容量**,避免覆盖还未使用的旧值。
public static int KnapsackOptimized(int[] weights, int[] values, int capacity){ int[] dp = new int[capacity + 1]; for (int i = 0; i < weights.Length; i++) { // 从后往前遍历容量 for (int w = capacity; w >= weights[i]; w--) { dp[w] = Math.Max(dp[w], dp[w - weights[i]] + values[i]); } } return dp[capacity];}上述优化不仅节省了大量内存,还提升了缓存局部性,使程序运行更快。这种“滚动数组”技巧在处理动态规划问题时非常常见,也是面试和竞赛中的高频考点。
希望这篇教程能帮你彻底搞懂背包问题!动手写一遍代码,你会理解得更深刻。加油,未来的算法高手!
本文由主机测评网于2025-12-03发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/2025122317.html