1. 背包问题概述
0/1背包问题可以说是算法领域最经典的动态规划入门题目了。我第一次接触这个问题是在大学算法课上,当时就被它简洁的问题描述和巧妙的解法所吸引。简单来说,问题描述是这样的:你有一个容量有限的背包,和一堆重量和价值各不相同的物品,每个物品只能选择拿或者不拿(这就是"0/1"的由来),问如何选择物品才能在不超过背包容量的前提下,使背包中物品的总价值最大。
这个问题看似简单,却包含了动态规划思想的精髓。在实际应用中,它可以是投资组合优化(有限的资金,不同的投资项目)、资源分配(有限的计算资源,不同的任务)等各种场景的抽象模型。我见过不少工程师在面试时被这个问题难住,其实只要理解了动态规划的核心思想,解决起来并不复杂。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 问题建模与解法分析
2.1 问题形式化描述
让我们先严格定义这个问题。给定:
- 背包容量W(正整数)
- n个物品,每个物品有两个属性:
- 重量w_i(正整数)
- 价值v_i(正整数)
我们需要找到一个子集S ⊆ {1,2,...,n},使得:
- Σ_{i∈S} w_i ≤ W(总重量不超过背包容量)
- Σ_{i∈S} v_i 最大化(总价值最大)
2.2 暴力解法及其局限
最直观的解法是枚举所有可能的物品组合,计算每种组合的总重量和总价值,然后选择满足重量限制且价值最大的组合。对于n个物品,共有2^n种可能的组合(每个物品有选或不选两种选择)。
这种方法在小规模问题上可行,但当n增大时,计算量呈指数级增长。比如n=100时,组合数已经超过了宇宙中原子的总数!显然,我们需要更高效的算法。
2.3 动态规划解法
动态规划通过将问题分解为子问题并存储子问题的解来避免重复计算。对于背包问题,我们定义dp[i][j]表示考虑前i个物品,背包容量为j时能获得的最大价值。
状态转移方程:
- 如果不选第i个物品:dp[i][j] = dp[i-1][j]
- 如果选第i个物品(前提是j ≥ w_i):dp[i][j] = dp[i-1][j-w_i] + v_i
- 取两者中的较大值
初始条件:
- dp[0][j] = 0(没有物品可选时价值为0)
- dp[i][0] = 0(背包容量为0时价值为0)
最终答案是dp[n][W]。
