动态规划分类: 
线性dp 
区间dp 
背包dp 
树形dp 
状态压缩dp 
数位dp 
计数型dp 
递推型dp 
概率型dp 
博弈型dp 
记忆化搜索 
 
动态规划思考方式: 
1.线性dp 
三角形最小路径和  
 
1 2 3 4 5 6 7 8 9 10 11 public int minimumTotal(List<List<Integer>> triangle) {     int len = triangle.size();     int[] dp = new int[len+1];     for (int i = len-1; i >=0 ; i--) {         for (int j = 0; j <=i ; j++) {             int num = triangle.get(i).get(j);             dp[j] = Math.min(dp[j],dp[j+1]) + num;         }     }     return dp[0]; } 
 
最长上升子序列  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 public int lengthOfLIS(int[] nums) {     int max = 0;     int[] dp = new int[nums.length];     for (int i = 0; i < nums.length; i++) {         int count = 0;         for (int j = 0; j <i ; j++) {             if(nums[j] < nums[i] && dp[j] > count) count = dp[j];         }         dp[i] = count + 1;         if(max < dp[i]) max = dp[i];     }     return max; } 
 
最长公共子序列  
 
1 2 3 4 5 6 7 8 9 10 11 12 public int longestCommonSubsequence(String text1, String text2) {     int n = text1.length();     int m = text2.length();     int[][] dp = new int[n+1][m+1];     for (int i = 1; i <=n ; i++) {         for (int j = 1; j <=m ; j++) {             if(text1.charAt(i-1) == text2.charAt(j-1)) dp[i][j] = dp[i-1][j-1] +1;             else dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);         }     }     return dp[n][m]; } 
 
2.区间dp 
区间DP 问题是指递推方程具有明显的区间关系,有左端点和右端点。 
如: 
 
最长回文子序列  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 public static int longestPalindromeSubseq(String s) {     int len = s.length();     char[] chars = s.toCharArray();     int[][] dp = new int[len][len];     for (int i = len-1; i >=0; i--) dp[i][i] = 1;    //base case     for (int i = len-1; i >=0; i--) {         for (int j = i+1; j <len ; j++) {             if(chars[i] == chars[j]) dp[i][j] = dp[i+1][j-1]+2;             else dp[i][j] = Math.max(dp[i+1][j],dp[i][j-1]);         }     }     return dp[0][len-1]; } 
 
3.背包dp 
背包DP 问题是指递推方程具有明显的限制条件 
在N中情况中 且满足某条件的 情况下 最优的选择情况 
如在背包重量不超过w的情况下,从N中物品中选择出 最有价值的物品 (01背包) 
详情参考 背包模板 
 
4.树形dp 
在树的结构上求解问题,大部分可以直接用递归+记忆化解决 
如果有明显的递推关系,可以尝试使用递推+递归 直接消除子问题区间的重复计算 即树形dp 
如: 
 
打家劫舍 III  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public int rob(TreeNode root) {     int[] res = dfs(root);     return Math.max(res[0],res[1]); } //0 代表不偷,1 代表偷 public int[] dfs(TreeNode root) {     if(root == null) return new int[]{0,0};     int[] res = {0,0};     int[] left = dfs(root.left);     int[] right = dfs(root.right);     res[0] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);     res[1] = left[0] + right[0] + root.val;     return res; } 
 
5.状压dp 1. 对于n个元素选与不选2种状态的问题(位运算) 
用2^n表示所有状态,第i位的状态 对应二进制数 第i位是0 还是1 
 
2. 对于n个元素k种状态的问题(幂运算或累乘) 
1.如果每种物品选择的数量是相同的 m种状态,可用m^n表示所有状态 
 
2.如果每种物品的选择数量不同 : 无论如何都要让每一种状态与一个数字一一对应 
 
526. 优美的排列  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int countArrangement(int N) {     int dp[]=new int[1<<N];     dp[0]=1;     for(int i=0;i<dp.length;i++){         int len=1;         for(int j=0;j<N;j++)             len+=i>>j&1;         for(int j=1;j<=N;j++){             if((i >> (j - 1) & 1)==0&& (j % len == 0 || len % j == 0)) {                 dp[i | (1 << j - 1)] += dp[i];   // 或运算,进行状态转移             }         }     }     return dp[(1<<N)-1]; } 
 
6.数位 DP 
给定一个闭区间[l,r],求这个区间中满足某种条件的数的总量 
 
7.计数型 DP 
62. 不同路径 I 
1 2 3 4 5 6 7 8 9 10 public static int uniquePaths(int m, int n) {     int[][] dp = new int[m+1][n+1];     dp[1][1] = 1;     for (int i = 1; i <=m ; i++) {         for (int j = 1; j <=n ; j++) {             dp[i][j] += dp[i-1][j] + dp[i][j-1];         }     }     return dp[m][n]; } 
 
63. 不同路径 II 
1 2 3 4 5 6 7 8 9 10 11 public int uniquePathsWithObstacles(int[][] grid) {     int[][] dp = new int[grid.length+1][grid[0].length+1];     if(grid[0][0] == 1) return 0;     dp[1][1] = 1;     for (int i = 1; i <=grid.length ; i++) {         for (int j = 1; j <=grid[0].length ; j++) {             if(grid[i-1][j-1] == 0) dp[i][j] += dp[i-1][j] + dp[i][j-1];         }     }     return dp[grid.length][grid[0].length]; } 
 
8.递推型 DP 
70. 爬楼梯 
1 2 3 4 5 6 7 8 9 10 11 12 13  public int climbStairs(int n) {     int tmp = 0;     int a = 1;     int b = 2;     if(n == 1) tmp = 1;     if(n == 2) tmp = 2;         for(int i=3;i<=n;i++){         tmp = a+b;         a = b;         b = tmp;          }     return tmp; } 
 
9.概率型 DP 
808. 分汤 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public double soupServings(int N) {     if(N>=4800) return 1.0;     int n = (int) Math.ceil(N/25.0);     double[][] dp = new double[n+1][n+1];     dp[0][0] = 0.5;     for (int i = 1; i <= n ; i++) {         dp[i][0] = 0;         dp[0][i] = 1;     }     for (int i = 1; i <= n; ++i){         int a1 = i - 4 > 0 ? i - 4 : 0;//不足4,按4算(实际上是不足100,按100算,然后分配完了,没有剩余)         int a2 = i - 3 > 0 ? i - 3 : 0;//不足3,按3算(实际上是不足75,按75算,然后分配完了,没有剩余)         int a3 = i - 2 > 0 ? i - 2 : 0;//不足2,按2算(实际上是不足50,按75算,然后分配完了,没有剩余)         int a4 = i - 1 > 0 ? i - 1 : 0;//不足1,按1算(实际上是不足25,按25算,然后分配完了,没有剩余)         for(int j = 1; j <= n; ++j) {             int b1 = j;             int b2 = j - 1 > 0 ? j - 1 : 0;//不足1,按1算(实际上是不足25,按25算,然后分配完了,没有剩余)             int b3 = j - 2 > 0 ? j - 2 : 0;//不足2,按2算(实际上是不足50,按75算,然后分配完了,没有剩余)             int b4 = j - 3 > 0 ? j - 3 : 0;//不足3,按3算(实际上是不足75,按75算,然后分配完了,没有剩余)             //状态转移方程:dp[i][j] = 0.25 * (dp[i-100][j] + dp[i-75][j-25] + dp[i-50][j-50] + dp[i-75][j-25])             //将N缩小为原来的25分之一的转移方程:dp[i][j] = 0.25 * (dp[i-4][j] + dp[i-3][j-1] + dp[i-2][j-2] + dp[i-3][j-1])             dp[i][j]= 0.25 * (dp[a1][b1] + dp[a2][b2] + dp[a3][b3] + dp[a4][b4]);         }     }     return dp[n][n]; } 
 
dfs解法1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static Map<String,Double> map; public double soupServings(int N) {     if(N>=4800) return 1;     map = new HashMap<>();     int n = (int) Math.ceil(N/25.0);     return dfs(n,n); } public static double dfs(int i,int j){     if(i<=0 && j<=0) return 0.5;     else if(i<=0 && j>0) return 1;     else if(i>0 && j<=0) return 0;     else if(map.containsKey(i + " " + j)) return map.get(i + " " + j);     else{         double sum = 0.25*(dfs(i-4,j)+dfs(i-3,j-1)+                 dfs(i-2,j-2)+dfs(i-1,j-3));         map.put(i + " " + j,sum);         return sum;     } } 
837. 新21点  反向递推
1 2 3 4 5 6 7 8 9 10 11 public double new21Game(int N, int K, int W) {     double[] dp = new double[N+W+1];     for (int i = K; i <=N ; i++) dp[i] = 1;     double sum = 0;     for (int i = 0; i <W ; i++) sum += dp[K+i];     for (int i = K-1; i >=0 ; i--) {         dp[i] = sum/W;         sum += dp[i] - dp[i+W];     }     return dp[0]; } 
 
后面待更新….