zoukankan      html  css  js  c++  java
  • 动态规划算法(后附常见动态规划为题及Java代码实现)

    一、基本概念

        动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。

    二、基本思想与策略

        基本思想与分治法类似,也是将待求解的问题分解为若干个子问题(阶段),按顺序求解子阶段,前一子问题的解,为后一子问题的求解提供了有用的信息。在求解任一子问题时,列出各种可能的局部解,通过决策保留那些有可能达到最优的局部解,丢弃其他局部解。依次解决各子问题,最后一个子问题就是初始问题的解。

        由于动态规划解决的问题多数有重叠子问题这个特点,为减少重复计算,对每一个子问题只解一次,将其不同阶段的不同状态保存在一个二维数组中。

        与分治法最大的差别是:适合于用动态规划法求解的问题,经分解后得到的子问题往往不是互相独立的(即下一个子阶段的求解是建立在上一个子阶段的解的基础上,进行进一步的求解)。

    以上都过于理论,还是看看常见的动态规划问题吧!!!

    三、常见动态规划问题

       1、找零钱问题

       有数组penny,penny中所有的值都为正数且不重复。每个值代表一种面值的货币,每种面值的货币可以使用任意张,再给定一个整数aim(小于等于1000)代表要找的钱数,求换钱有多少种方法。
    给定数组penny及它的大小(小于等于50),同时给定一个整数aim,请返回有多少种方法可以凑成aim。
    测试样例:
    [1,2,4],3,3
    返回:2

    解析:设dp[n][m]为使用前n中货币凑成的m的种数,那么就会有两种情况:

                 使用第n种货币:dp[n-1][m]+dp[n-1][m-peney[n]]

                  不用第n种货币:dp[n-1][m],为什么不使用第n种货币呢,因为penney[n]>m。

            这样就可以求出当m>=penney[n]时 dp[n][m] = dp[n-1][m]+dp[n-1][m-peney[n]],否则,dp[n][m] = dp[n-1][m]

    代码如下:

    [java] view plain copy
     
    1. <span style="font-size:18px;">import java.util.*;  
    2.   
    3. public class Exchange {  
    4.     public int countWays(int[] penny, int n, int aim) {  
    5.         // write code here  
    6.         if(n==0||penny==null||aim<0){  
    7.          return 0;     
    8.         }  
    9.         int[][] pd = new int[n][aim+1];  
    10.         for(int i=0;i<n;i++){  
    11.          pd[i][0] = 1;     
    12.         }  
    13.         for(int i=1;penny[0]*i<=aim;i++){  
    14.          pd[0][penny[0]*i] = 1;     
    15.         }  
    16.         for(int i=1;i<n;i++){  
    17.             for(int j=0;j<=aim;j++){  
    18.                 if(j>=penny[i]){  
    19.                     pd[i][j] = pd[i-1][j]+pd[i][j-penny[i]];  
    20.                 }else{  
    21.                     pd[i][j] = pd[i-1][j];  
    22.                 }  
    23.             }  
    24.         }  
    25.         return pd[n-1][aim];  
    26.     }  
    27. }</span>  


    2、走方格问题

      有一个矩阵map,它每个格子有一个权值。从左上角的格子开始每次只能向右或者向下走,最后到达右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和。
    给定一个矩阵map及它的行数n和列数m,请返回最小路径和。保证行列数均小于等于100.
    测试样例:
    [[1,2,3],[1,1,1]],2,3
    返回:4

    解析:设dp[n][m]为走到n*m位置的路径长度,那么显而易见dp[n][m] = min(dp[n-1][m],dp[n][m-1]);

    代码如下:

    [java] view plain copy
     
    1. <span style="font-size:18px;">import java.util.*;  
    2.   
    3. public class MinimumPath {  
    4.     public int getMin(int[][] map, int n, int m) {  
    5.         // write code here  
    6.        int[][] dp = new int[n][m];  
    7.         for(int i=0;i<n;i++){  
    8.             for(int j=0;j<=i;j++){  
    9.              dp[i][0]+=map[j][0];      
    10.             }  
    11.         }  
    12.         for(int i=0;i<m;i++){  
    13.             for(int j=0;j<=i;j++){  
    14.              dp[0][i]+=map[0][j];      
    15.             }  
    16.         }  
    17.         for(int i=1;i<n;i++){  
    18.             for(int j=1;j<m;j++){  
    19.              dp[i][j] = min(dp[i][j-1]+map[i][j],dp[i-1][j]+map[i][j]);     
    20.             }  
    21.         }  
    22.         return dp[n-1][m-1];  
    23.     }  
    24.     public int min(int a,int b){  
    25.         if(a>b){  
    26.          return b;     
    27.         }else{  
    28.          return a;     
    29.         }  
    30.     }  
    31. }</span>  


    3、走台阶问题

    有n级台阶,一个人每次上一级或者两级,问有多少种走完n级台阶的方法。为了防止溢出,请将结果Mod 1000000007
    给定一个正整数int n,请返回一个数,代表上楼的方式数。保证n小于等于100000。
    测试样例:
    1
    返回:1

    解析:这是一个非常经典的为题,设f(n)为上n级台阶的方法,要上到n级台阶的最后一步有两种方式:从n-1级台阶走一步;从n-1级台阶走两步,于是就有了这个公式f(n) = f(n-1)+f(n-2);

    代码如下:

    [java] view plain copy
     
    1. <span style="font-size:18px;">import java.util.*;  
    2.   
    3. public class GoUpstairs {  
    4.     public int countWays(int n) {  
    5.         // write code here  
    6.         if(n<=2)  
    7.             return n;  
    8.         int f = 1%1000000007;  
    9.         int s = 2%1000000007;  
    10.         int t = 0;  
    11.         for(int i=3;i<=n;i++){  
    12.          t = (f+s)%1000000007;  
    13.          f = s;  
    14.          s = t;  
    15.         }  
    16.        return t;   
    17.     }  
    18. }</span>  


    4、最长公共序列数

    给定两个字符串A和B,返回两个字符串的最长公共子序列的长度。例如,A="1A2C3D4B56”,B="B1D23CA45B6A”,”123456"或者"12C4B6"都是最长公共子序列。
    给定两个字符串A和B,同时给定两个串的长度n和m,请返回最长公共子序列的长度。保证两串长度均小于等于300。
    测试样例:
    "1A2C3D4B56",10,"B1D23CA45B6A",12
    返回:6

    解析:设dp[n][m] ,为A的前n个字符与B的前m个字符的公共序列长度,则当A[n]==B[m]的时候,dp[i][j] = max(dp[i-1][j-1]+1,dp[i-1][j],dp[i][j-1]),否则,dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);

    代码如下:

    [java] view plain copy
     
      1. <span style="font-size:18px;">import java.util.*;  
      2.   
      3. public class LCS {  
      4.     public int findLCS(String A, int n, String B, int m) {  
      5.         // write code here  
      6.         int[][] dp = new int[n][m];  
      7.         char[] a = A.toCharArray();  
      8.         char[] b = B.toCharArray();  
      9.        for(int i=0;i<n;i++){  
      10.            if(a[i]==b[0]){  
      11.                dp[i][0] = 1;  
      12.                for(int j=i+1;j<n;j++){  
      13.                    dp[j][0] = 1;  
      14.                }  
      15.                break;  
      16.            }  
      17.              
      18.        }  
      19.          for(int i=0;i<m;i++){  
      20.            if(a[0]==b[i]){  
      21.                dp[0][i] = 1;  
      22.                for(int j=i+1;j<m;j++){  
      23.                    dp[0][j] = 1;  
      24.                }  
      25.                break;  
      26.            }  
      27.              
      28.        }  
      29.        for(int i=1;i<n;i++){  
      30.            for(int j=1;j<m;j++){  
      31.                if(a[i]==b[j]){  
      32.                   dp[i][j] = max(dp[i-1][j-1]+1,dp[i-1][j],dp[i][j-1]);  
      33.                }else{  
      34.                    dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);  
      35.                }  
      36.                      
      37.            }  
      38.        }   
      39.           
      40.         return dp[n-1][m-1];  
      41.     }  
      42.     public int max(int a,int b,int c){  
      43.         int max = a;  
      44.         if(b>max)  
      45.             max=b;  
      46.         if(c>max)  
      47.             max = c;  
      48.         return max;  
      49.     }  
      50. }</span>  
  • 相关阅读:
    66. 缓存字节流
    65. 练习(拷贝图片--边读边写)
    64. 输出字节流(FileOutputStream)
    63. (FileInputStream)输入字节流
    62. File类常用方法
    61. File类
    60. 枚举
    快速排序
    归并排序
    初级排序算法
  • 原文地址:https://www.cnblogs.com/yangchunchun/p/7265699.html
Copyright © 2011-2022 走看看