动态规划一般是用来解决以下几类问题:
1、计数
2、求最大值、最小值
3、求事件的存在性

动态规划DP - 图1

动态规划的一般解题步骤
1、确定状态
· 确定最后一步
· 转化成为一个个子问题
2、写出状态转移方程
3、确定开始和边界的条件
4、计算顺序

说了跟没说一样(也太抽象了)
不如直接做一道题看看

1、LintCode 669 换硬币

image.png

1、确定状态

1.1”最后一步”

首先我们把最后一个硬币的面值是ak,那么前面的硬币值肯定是27-ak
动态规划DP - 图3
接下下,”最后一步”还有两个注意的关键点

动态规划DP - 图4

1.2 化解子问题

动态规划DP - 图5

还有个问题,我们还不知ak是多少,所以需要
动态规划DP - 图6

这样我们的第一步就完成了

2、确定转移方程

根据第一步的状态,我们这样设置,(注意,上面的大括号在这一步已经变成方括号了)
动态规划DP - 图7

完成这一步可以先喝杯咖啡了

3、确定开始和边界条件

首先我们的硬币肯定从f[0]即凑出0元需要0枚硬币,

接下来是边界问题:

动态规划DP - 图8

4、计算顺序

有了我们前面分析的前三步,接下来就是计算顺序了,其实就是从f[0]开始计算而已,并把他们都记录下来

动态规划DP - 图9

结果其实是这样的

动态规划DP - 图10

5、代码

  1. package test
  2. public class application {
  3. public static void main(String[] args) {
  4. int money = 27; //假设需要拼凑的总金额为27元
  5. int[] coin = new int[3]; //拥有的硬币面额
  6. int[] dp = new int[money+1]; // 动态规划用到的数组
  7. coin[0] = 2;
  8. coin[1] = 5;
  9. coin[2] = 7;
  10. dp[0] = 0; // 拼凑出0元所需要的硬币数为0
  11. for(int i = 1;i <= money;i++){
  12. dp[i] = Integer.MAX_VALUE; // 默认拼凑出i元所需要的硬币数为无穷大(无法拼凑出来)
  13. for(int j = 0;j < coin.length;j++){
  14. if(i - coin[j] >= 0 && dp[i-coin[j]] != Integer.MAX_VALUE){
  15. dp[i] = Math.min(dp[i],dp[i-coin[j]] + 1);
  16. }
  17. }
  18. }
  19. if(dp[money] == Integer.MAX_VALUE){
  20. System.out.println("不能够拼凑出来");
  21. }
  22. else{
  23. System.out.println(dp[money]);
  24. }
  25. }
  26. }

2、LintCode 114 不同的路径

image.png

1、确定状态

1.1 最后一步

动态规划DP - 图12

1.2 化解子问题

动态规划DP - 图13

2、状态转移方程

设F[i][j] 表示到 (i,j) 时方法数,那么,
状态转移方程:

F[i][j] = F[i-1][j] + F[i][j-1]


3、确定开始和边界条件

开始:F[0][0] = 1,规定到初始点的方法数只有一种
边界:

  1. - i = 0 的时候,对应地图中的第0行,由于只能向下或者向右移动,那么到达第0行的任意一列的方法数应该都只有一种。
  2. - 同理,当 j = 0 的时候,对应地图中的第0列,到达第0列的任意一行的方法数应该都只有一种。

即:

F[0][j] = 1 F[i][0] = 1

4、计算顺序

动态规划DP - 图14

5、代码

  1. package test2;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Queue;
  5. import java.util.Stack;
  6. public class application {
  7. public static void main(String[] args) {
  8. int m = 3;
  9. int n = 3;
  10. int [][]dp = new int[m+1][n+1];
  11. dp[0][0] = 1;
  12. for(int i = 0;i < m;i++){
  13. for(int j = 0;j < n;j++){
  14. if(i == 0 || j == 0){
  15. dp[i][j] = 1;
  16. }
  17. else {
  18. dp[i][j] = dp[i-1][j] + dp[i][j-1];
  19. }
  20. }
  21. }
  22. System.out.println(dp[m-1][n-1]);
  23. }
  24. }

3、LintCode 115 不同的路径Ⅱ

image.png

  1. public class Solution {
  2. /**
  3. * @param obstacleGrid: A list of lists of integers
  4. * @return: An integer
  5. */
  6. public int uniquePathsWithObstacles(int[][] obstacleGrid) {
  7. // write your code here
  8. int m = obstacleGrid.length;
  9. int n = obstacleGrid[0].length;
  10. int[][]dp = new int[m+1][n+1];
  11. dp[0][0] = 1;
  12. if(obstacleGrid[0][0] == 1)
  13. return 0;
  14. for(int i = 0;i < m;i++){
  15. for(int j = 0;j < n;j++){
  16. if((i == 0 || j == 0) && obstacleGrid[i][j] != 1){
  17. dp[i][j] = 1;
  18. }
  19. if(obstacleGrid[i][j] == 1){
  20. dp[i][j] = 0;
  21. }
  22. }
  23. }
  24. for(int i = 0;i < m;i++){
  25. for(int j = 0;j < n;j++){
  26. if(i == 0 && obstacleGrid[i][j] == 1){
  27. for(int k = j;k < n;k++){
  28. dp[i][k] = 0;
  29. }
  30. break;
  31. }
  32. if(j == 0 && obstacleGrid[i][j] == 1){
  33. for(int k = i;k < m;k++){
  34. dp[k][j] = 0;
  35. }
  36. }
  37. }
  38. }
  39. for(int i = 1;i < m;i++){
  40. for(int j = 1;j < n;j++){
  41. if(obstacleGrid[i][j] == 0)
  42. dp[i][j] = dp[i-1][j] + dp[i][j-1];
  43. }
  44. }
  45. return dp[m-1][n-1];
  46. }
  47. }