動態規劃經典例題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)
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));
}
}