1. 程式人生 > >動態規劃經典例題java實現

動態規劃經典例題java實現

    動態規劃演算法通常基於一個遞推公式及一個或多個初始狀態。當前子問題的解將由上一次子問題的解推出。使用動態規劃來解題只需要多項式時間複雜度,因此它比回溯法、暴力法等要快許多。
    解決動態規劃問題的關鍵是要找到狀態轉移方程。將問題分解成最小的子問題,找到由子問題到全域性問題的解決方案。

    可以採用動態規劃求解的問題的一般要具有3個性質:
    (1) 最優化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最優化原理。
    (2) 無後效性:即某階段狀態一旦確定,就不受這個狀態以後決策的影響。也就是說,某狀態以後的過程不會影響以前的狀態,只與當前狀態有關。
   (3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被多次使用到。(該性質並不是動態規劃適用的必要條件,但是如果沒有這條性質,動態規劃演算法同其他演算法相比就不具備優勢)。

    求解動態規劃問題的基本步驟:
    (1)分析最優解的性質,並刻畫其結構特徵。
    (2)遞迴的定義最優解。
    (3)以自底向上或自頂向下的記憶化方式(備忘錄法)計算出最優值
    (4)根據計算最優值時得到的資訊,構造問題的最優解

例1:有n級臺階,一個人每次上一級或者兩級,問有多少種走完n級臺階的方法。

在這個問題上,我們讓f(n)表示走上n級臺階的方法數。那麼當n為1時,f(n) = 1,n為2時,f(n) =2,就是說當臺階只有一級的時候,方法數是一種,臺階有兩級的時候,方法數為2。那麼當我們要走上n級臺階,必然是從n-1級臺階邁一步或者是從n-2級臺階邁兩步,所以到達n級臺階的方法數必然是到達n-1級臺階的方法數加上到達n-2級臺階的方法數之和。即f(n) = f(n-1)+f(n-2)

,我們用dp[n]來表示動態規劃表,dp[i],i>0,i<=n,表示到達i級臺階的方法數。

public class steps {
static int[] array;
public static int step(int n){
if(n<=2)  
           return n;  
       int f = 1%2147483647;  
       int s = 2%2147483647;  
       int t = 0;  
       for(int i=3;i<=n;i++){  
        t = (f+s)%2147483647;  
        f = s;  
        s = t;  
       }  
      return t;   
}
public static void main(String[] args) {
System.out.println(step(5));
}
}

例2:有一個矩陣map,它每個格子有一個權值。從左上角的格子開始每次只能向右或者向下走,最後到達右下角的位置,
 路徑上所有的數字累加起來就是路徑和,返回所有的路徑中最小的路徑和。

我們必須注意到的一點是,到達一個格子的方式最多隻有兩種:從左邊來的(除了第一列)和從上邊來的(除了第一行)。因此為了求出到達當前格子的最短路徑,我們就要先去考察那些能到達當前這個格子的格子,到達它們的最短路徑。經過上面的分析,很容易可以得出問題的狀態和狀態轉移方程。狀態S[i][j]表示我們走到(i, j)這個格子時,最短的路徑。那麼,狀態轉移方程如下:
S[i][j]=A[i][j] + min(S[i-1][j], if i>0 ; S[i][j-1], if j>0)
其中i代表行,j代表列,下標均從0開始。

public class MinPath {
	public static int getMin(int[][] map, int n, int m) {   
       int[][] dp = new int[n][m];  
        for(int i=0;i<n;i++){        //初始化第一列的值
            for(int j=0;j<=i;j++){  
             dp[i][0]+=map[j][0];      
            }  
        }  
        for(int i=0;i<m;i++){       //初始化第一行的值
            for(int j=0;j<=i;j++){  
             dp[0][i]+=map[0][j];      
            }  
        }  
        for(int i=1;i<n;i++){  
            for(int j=1;j<m;j++){  
             dp[i][j] = min(dp[i][j-1]+map[i][j],dp[i-1][j]+map[i][j]);     
            }  
        }  
        return dp[n-1][m-1];  
    }  
    public static int min(int a,int b){  
        if(a>b){  
         return b;     
        }else{  
         return a;     
        }  
    }  
	public static void main(String[] args) {
	    Scanner sc=new Scanner(System.in);
	    int m=sc.nextInt();
	    int n=sc.nextInt();
	    int map[][]=new int[m][n];
	    for(int i=0;i<m;i++){
	    	for(int j=0;j<n;j++){
	    		map[i][j]=sc.nextInt();
	    	}
	    }	    System.out.println(getMin(map,m,n));
	}

例3:給定兩個字串A和B,返回兩個字串的最長公共子序列的長度。

設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] =max(dp[i-1][j],dp[i][j-1]);

public class LCS {
	public static int findLCS(String A, int n, String B, int m) {  
        int[][] dp = new int[n][m];  
        char[] a = A.toCharArray();  
        char[] b = B.toCharArray();  
       for(int i=0;i<n;i++){  
           if(a[i]==b[0]){  
               dp[i][0] = 1;  
               for(int j=i+1;j<n;j++){  
                   dp[j][0] = 1;  
               }  
               break;  
           }             
       }  
         for(int i=0;i<m;i++){  
           if(a[0]==b[i]){  
               dp[0][i] = 1;  
               for(int j=i+1;j<m;j++){  
                   dp[0][j] = 1;  
               }  
               break;  
           }              
       }  
       for(int i=1;i<n;i++){  
           for(int j=1;j<m;j++){  
               if(a[i]==b[j]){  
                  dp[i][j] = max(dp[i-1][j-1]+1,dp[i-1][j],dp[i][j-1]);  
               }else{  
                   dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);  
               }                    
           }  
       }           
        return dp[n-1][m-1];  
    }  
    public static int max(int a,int b,int c){  
        int max = a;  
        if(b>max)  
            max=b;  
        if(c>max)  
            max = c;  
        return max;  
    }  
	public static void main(String[] args) {		    System.out.println(findLCS("1A3C3D4B56",10,"B1D23CA45B6A",12));
	}
}