动态规划入门指南
什么是动态规划?
动态规划(Dynamic Programming,简称DP)是一种解决复杂问题的方法,它通过把原问题分解为相对简单的子问题,然后利用子问题的最优解来推导出原问题的最优解。动态规划常常适用于有重叠子问题和最优子结构性质的问题。
重叠子问题是指在求解原问题时,需要反复求解相同或者相似的子问题。如果不使用动态规划,那么这些子问题就会被多次重复计算,导致效率低下。动态规划的一个核心思想就是记住已经求解过的子问题的答案,避免重复计算,从而提高效率。
最优子结构是指原问题的最优解可以由子问题的最优解推导出来。如果一个问题具有最优子结构性质,那么就可以使用动态规划来求解。动态规划的另一个核心思想就是根据状态转移方程来递推出原问题的最优解,从而降低复杂度。
动态规划的基本步骤
一般来说,使用动态规划来求解一个问题,需要遵循以下四个步骤:
- 定义状态:状态是指描述原问题或者子问题的特征变量,通常用一个或者多个变量来表示。比如在背包问题中,状态可以用两个变量来表示:当前物品的编号和当前背包的容量。
- 找出状态转移方程:状态转移方程是指描述状态之间如何转换的数学表达式,通常用递归或者迭代的方式来表示。比如在背包问题中,状态转移方程可以表示为:
dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])
,其中dp[i][j]
表示前i
个物品放入容量为j
的背包中能得到的最大价值,w[i]
和v[i]
分别表示第i
个物品的重量和价值。 - 确定边界条件:边界条件是指状态转移方程中不能递推或者迭代的特殊情况,通常用已知或者显然的值来表示。比如在背包问题中,边界条件可以表示为:当
i=0
或者j=0
时,dp[i][j]=0
,即没有物品或者没有容量时,背包中的价值为0。 - 计算并返回结果:根据状态转移方程和边界条件,从小到大或者从大到小地计算出所有状态的值,并返回原问题所需的结果。比如在背包问题中,可以从左上角到右下角地填充二维数组,并返回右下角的值作为最终答案。
动态规划的示例代码
下面给出一个使用动态规划求解背包问题的示例代码
// 定义物品类 class Item { String name; // 物品名称 int weight; // 物品重量 int value; // 物品价值 public Item(String name, int weight, int value) { this.name = name; this.weight = weight; this.value = value; } } // 定义背包问题类 public class KnapsackProblem { // 定义物品列表 static Item[] items = { new Item("水", 3, 10), new Item("书", 1, 3), new Item("食物", 2, 9), new Item("小刀", 3, 4), new Item("衣物", 2, 5), new Item("手机", 1, 10) }; // 定义背包最大容量 static int max_capacity = 6; // 定义状态数组 static int[][] dp = new int[items.length + 1][max_capacity + 1]; // 找出状态转移方程并计算所有状态 public static void solve() { for (int i = 1; i <= items.length; i++) { for (int j = 1; j <= max_capacity; j++) { int weight = items[i-1].weight; // 获取当前物品重量 int value = items[i-1].value; // 获取当前物品价值 if (weight > j) { dp[i][j] = dp[i-1][j]; // 大于直接取上一次最优结果 此时i-1代表上一行 } else { dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weight] + value); // 使用内置函数Math.max(),将上一次最优结果与当前物品价值+剩余空间可利用价值做对比取最大值 } } } } // 返回结果 public static void printResult() { System.out.println("最大价值为:" + dp[items.length][max_capacity]); // 输出状态数组 for (int[] row : dp) { for (int cell : row) { System.out.print(cell + " "); } System.out.println(); } } public static void main(String[] args) { solve(); printResult(); } }