经过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划经常适用于有重叠子问题和最优子结构性质的问题。html
这里先看一道LeetCode题。从这道题来学习如何使用动态规划。java
Coin Change算法
给定不一样面额的硬币 coins 和一个总金额 amount。 编写一个函数来计算能够凑成总金额所需的最少的硬币个数。 若是没有任何一种硬币组合能组成总金额,返回 -1。
示例数据结构
输入: coins = [1, 2, 5], amount = 11 输出: 3 解释: 11 = 5 + 5 + 1
该题是一个求最大最小的动态规划算法题。ide
与递归解法相比,没有重复计算。函数
肯定状态须要有两个注意的点:最后一步
、子问题
学习
1.最后一步spa
确定是$k$枚硬币加起来等于11。最后一枚硬币值假设是$a_k$,则剩下的$k-1$枚硬币的值为$11-a_k$。code
因为是最优解,则11-$a_k$的硬币数必定是最少。
2.子问题
将原问题转换为子问题,最少用多少枚硬币拼出$11-a_k$
那么$a_k$究竟是多少,由于有3枚硬币,因此只多是一、二、5中的一个。
子问题方程以下:
$f(11) = min{f(11-1)+1,f(11-2)+1,f(11-5)+1}$
f(11)为拼出面值为11所需的最少硬币数。
根据以上,使用递归的解法:
public class Dp1 { public int getMinCoin(int X) { if (X == 0) return 0; int res = 10000; if (X >= 1) { res = Math.min(getMinCoin(X - 1)+1, res); } if (X >= 2) { res = Math.min(getMinCoin(X - 2)+1, res); } if (X >= 5) { res = Math.min(getMinCoin(X - 5)+1, res); } return res; } public static void main(String[] args) { Dp1 dp1 = new Dp1(); int result = dp1.getMinCoin(11); System.out.println(result); } }
使用递归来解决,有比较多的重复计算,效率比较低。
动态规划会保存计算结果,来避免递归重复计算的问题。
动态规划的解法
状态f[X]表示,面值为X所需的最小硬币数。 对于任意的X,知足 $f[X] = min{f[X-1]+1,f[X-2]+1,f[X-5]+1}$
意思就是获取面值大小为X最少须要的硬币数 = 从(最后一个硬币选1时,剩下的要凑面值为X-1所须要的最少硬币数,由于最后一个硬币选了1,因此硬币数要+1,即f[x-1]+1)、(f[X-2]+1)、(f[X-5]+1)中选择一个最少的硬币数。
设置初始值,考虑边界状况。
从上到下,从左到右。
class Solution { public int coinChange(int[] coins, int amount) { int[] f = new int[amount+1]; int coin_num = coins.length; //初始条件 f[0] = 0; //f[x] = min{f[x-c1]+1,f[x-c2]+1,f[x-c3]+1} for(int x = 1;x<=amount;x++){ f[x] = Integer.MAX_VALUE; for(int i = 0;i<coin_num;i++){ // 考虑输入[2],4,则须要保证f[x-coins[i]] != Integer.MAX_VALUE,即f[x-coins[i]]必需要是存在的状态 if(x >=coins[i] && f[x-coins[i]] != Integer.MAX_VALUE){ f[x] = Math.min(f[x-coins[i]]+1,f[x]); } } } // 考虑输入[2],3,则amount = -1 if(f[amount] == Integer.MAX_VALUE){ return -1; } return f[amount]; } }
动态规划入门 Introduction to Dynamic Programming ACM专题讲解:DP动态规划 算法数据结构面试通关(经验全集)