日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

动态规划经典问题

發布時間:2024/3/24 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划经典问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

from:https://segmentfault.com/a/1190000004498566#articleHeader4

動態規劃

代碼實現在https://github.com/Jensenczx/CodeEveryday
維基百科對動態規劃的定義

動態規劃(英語:Dynamic programming,簡稱DP)是一種在數學、計算機科學和經濟學中使用的,通過把原問題分解為相對簡單的子問題的方式求解復雜問題的方法。動態規劃常常適用于有重疊子問題[1]和最優子結構性質的問題,動態規劃方法所耗時間往往遠少于樸素解法。動態規劃背后的基本思想非常簡單。大致上,若要解一個給定問題,我們需要解其不同部分(即子問題),再合并子問題的解以得出原問題的解。

通常許多子問題非常相似,為此動態規劃法試圖僅僅解決每個子問題一次,從而減少計算量:一旦某個給定子問題的解已經算出,則將其記憶化(en:memoization)存儲,以便下次需要同一個子問題解之時直接查表。這種做法在重復子問題的數目關于輸入的規模呈指數增長時特別有用.

簡言之動態規劃的思路是通過尋找最優子結構同時記錄最優結構,從而將復雜的大問題轉化為小問題的求解過程,最近針對于動態規劃做了些練習,找到些解題的思路和感覺,下面針對于幾個問題來逐步的分析下動態規劃。

動態規劃問題實例

解決動態規劃類問題,分為兩步:1.確定狀態,2.根據狀態列狀態轉移方程
確定該狀態上可以執行的操作,然后是該狀態和前一個狀態或者前多個狀態有什么關聯,通常該狀態下可執行的操作必定是關聯到我們之前的幾個狀態。

1.數字三角形(典型的二維數組問題)

問題描述
給定一個數字三角形,找到從頂部到底部的最小路徑和。每一步可以移動到下面一行的相鄰數字上。

[2],
[3,4],
[6,5,7],
[4,1,8,3]

從頂到底部的最小路徑和為11 ( 2 + 3 + 5 + 1 = 11)。

有一個像這樣的數字三角形:
         

? ? ? ? ? ? ? ? ? ? ? ? 7
      3 8
     8 1 0
    2 7 4 4
   4 5 2 6 5

從頂點開始,每個數字向下層走只能有左下和右下兩個方向,求出到達最后一行時最大的路徑之和.(原題)

狀態轉移方程:dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+a[i][j];

---最小值問題轉移方程:dp[i][j]=min(dp[i-1][j-1],dp[i-1][j])+a[i][j];//注意邊界!!!

如果采用樸素算法,我們需要記錄每次的行走軌跡,然后對其大小進行比較,最終得出結果,行走軌跡的統計是呈現指數遞增的,所以我們要采用動態規劃的方法來解決。根據我們的解決方法,先確定狀態,也就是每次向下走的一步即為一個狀態,然后是狀態轉移方程,從上一個狀態到下一個狀態,如果確定最優,當前狀態的結果,取決于上一個狀態,找到上一個狀態,然后確定上一個狀態到當前狀態轉移的方程。記錄下每一個狀態,我們通過一個二維數組來實現。----典型的兩層循環

public int minimumTotal(int[][] triangle) {// write your code hereif(triangle==null||triangle.length==0)return 0;int len = triangle.length;//用來記錄每一步的狀態int [][] cost = new int[len][len];//初始化cost[0][0]=triangle[0][0];//第一行為自身for(int i=1; i<len; i++){//i=1---后面需要減一for(int j=0; j<triangle[i].length; j++){//遍歷第i行//計算上一個狀態的時候,防止出現越界問題int lower = max(0,j-1);//防止j-1<0;int upper = min(j,triangle[i-1].length-1);//狀態轉移方程cost[i][j]= min(cost[i-1][lower],cost[i-1][upper])+triangle[i][j];}}int minCost = Integer.MAX_VALUE;for(int k=0; k<triangle[len-1].length; k++){minCost = min(minCost,cost[len-1][k]);}return minCost;}

2.背包問題兩講

這里解決了兩張背包問題,一個是確定最多可以裝的下多少的背包盛放物品問題,還有一個是背包中放置的物品具有價值,要來確定其價值為多少。解決方法都是通過動態規劃來解決。

背包問題1
問題描述
在n個物品中挑選若干物品裝入背包,最多能裝多滿?假設背包的大小為m,每個物品的大小為A[i]:m=30,A[17,5,6,7,10]---最滿為28

首先尋找狀態,確定將什么作為狀態,記錄狀態,有背包和物品,物品有放和不放兩種狀態,放置的時候可能會對應各種容量,當前的容量下可以放置進的最多的物品取決于上一個物品放置時在該狀態下所能夠達到的最大狀態和當前物品的的大小,這樣我們在最后,就可以得到每種容量下,所能放置的物品的最大數量。

public int backPack(int m, int[] A) {//m--背包大小---A---每個物品// write your code hereif (A == null || 0 == A.length || m == 0)return 0;//邊界條件int len = A.length;//物品個數5//初始化了一個數組,int[][] sum = new int[len][m+1];//容量for(int i=0;i<len;i++){//邊界為0初始化為0sum[i][0] = 0;//第i個商品初重}for(int j=0;j<m+1;j++){//A[0]=17,J,0-30if(j>=A[0]){sum[0][j] = A[0];}} for(int i=1;i<len;i++){for(int j=1;j<m+1;j++){if(j>=A[i]){sum[i][j] = max(sum[i-1][j], sum[i-1][j-A[i]]+A[i]);}else{sum[i][j] = sum[i-1][j];}}}return sum[len-1][m];}

背包問題2
問題描述
給出n個物品的體積A[i]和其價值V[i],將他們裝入一個大小為m的背包,最多能裝入的總價值有多大?

考慮到價值問題,狀態不發生變化,只是對于狀態我們所記錄的內容方式變化,我們現在記錄的是其價值,而不是其放置的物品的大小。

public int backPackII(int m, int[] A, int V[]) {// write your code hereif(m==0||A==null||V==null||0==A.length)return 0;int len = A.length;int [][]val = new int[len][m+1];for(int i=0;i<len; i++){val[i][0]=0;}for(int i=0; i<m+1; i++){if(i>=A[0])val[0][i]=V[0];}for(int i=1; i<len; i++){for(int j=1;j<m+1; j++){if(j>=A[i]){val[i][j] = max(val[i-1][j],val[i-1][j-A[i]]+V[i]);}else{val[i][j]=val[i-1][j];}}}return val[len-1][m];}

3.公共子序列,公共子串問題

公共子串
給出兩個字符串,找到最長公共子串,并返回其長度

狀態,字符串的每一位對應另一個字符串的每一個位置,因此通過一個二維數組來表示這每一個狀態位,然后是找狀態轉移方程,轉移方程即為其前一個位置的前一個的比對的結果累計當前的結果,如果相同則加1,否則為0

public int longestCommonSubstring(String A, String B) {// write your code hereif(A==null||B==null||A.length()==0||B.length()==0)return 0;int lenOfA = A.length();int lenOfB = B.length();//狀態記錄結構int[][] longSubString = new int[lenOfB][lenOfA];int max = 0;for(int i=0; i<lenOfA; i++){if(B.charAt(0)==A.charAt(i)){longSubString[0][i] = 1;max = 1;}}for(int i=1; i<lenOfB; i++){for(int j=0; j<lenOfA; j++){//狀態轉移if(B.charAt(i)==A.charAt(j)){if(j-1>=0)longSubString[i][j] = longSubString[i-1][j-1]+1;else longSubString[i][j]=1;max = Max(longSubString[i][j],max);}}}return max;}

公共子序列
給出兩個字符串,找到最長公共子序列(LCS),返回LCS的長度。

子序列和子串的區別在于,其值不是僅僅取決于其上一個位置的對應于比對的位置的狀態,而是要尋找最大的前面的狀態值中最大的一個。

public int longestCommonSubsequence(String A, String B) {// write your code hereif(A==null||B==null||A.length()==0||B.length()==0)return 0;int lenOfA = A.length();int lenOfB = B.length();int [][] subsLen = new int[lenOfB][lenOfA];int max=0;for(int i=0; i<lenOfA; i++){if(A.charAt(i)==B.charAt(0)){subsLen[0][i]=1;max = 1;}} for(int i=1; i<lenOfB; i++){for(int j=0; j<lenOfA; j++){if(A.charAt(j)==B.charAt(i)){subsLen[i][j]=Max(subsLen,i-1,j-1)+1;if(subsLen[i][j]>max)max = subsLen[i][j];}}}return max;}public int Max(int[][] array,int end1,int end2){if(end2<0)return 0;int max = array[0][0];for(int i=0; i<=end1; i++){for(int j=0; j<=end2; j++){if(array[i][j]>max)max = array[i][j];}}return max;}

4.打劫房屋

問題描述
假設你是一個專業的竊賊,準備沿著一條街打劫房屋。每個房子都存放著特定金額的錢。你面臨的唯一約束條件是:相鄰的房子裝著相互聯系的防盜系統,且當相鄰的兩個房子同一天被打劫時,該系統會自動報警。
給定一個非負整數列表,表示每個房子中存放的錢, 算一算,如果今晚去打劫,你最多可以得到多少錢 在不觸動報警裝置的情況下。

我們可以在通過一個數組來記錄下來,我們在每個位置打劫,所能得到的錢,在求下一個狀態的時候,遍歷前面的與其相隔的所有狀態,然后找到一個最大的,但是復雜度比較到達到了n2,空間復雜度為n,對于狀態,我們需要記錄的只有其前一個,還有與其相隔的所有狀態的最大值,因此通過兩個數字來表示即可。具體轉化方式見代碼實現。

public long houseRobber(int[] A) {// write your code hereif(A==null||A.length==0)return 0;int len = A.length;if(len==1)return A[0];long max1 = A[0];long max2 = A[1];for(int i=2; i<len; i++){long tmp = max2;max2 = max1+A[i];max1 = tmp;//在計算最大值的時候的一個轉化if(max2<max1)max2 = max1;}return Max(max1,max2);}public long Max(long a,long b){return a>b?a:b;}

5.編輯距離

題目描述
給出兩個單詞word1和word2,計算出將word1 轉換為word2的最少操作次數。

你總共三種操作方法:

  • 插入一個字符

  • 刪除一個字符

  • 替換一個字符

三種操作,因此我們在一個狀態上面可以進行三種狀態的變化,確定每一個狀態,通過第二個字符串和第一個字符串的每一個位置的對應作為一個狀態,處在該狀態上,我們可以進行的操作,改,進行改操作,那么與之關聯的前一個狀態是其前一個字符對應另一個字符串的當前對應的前一個字符,增,則是說當前字符串的當前位對應到前一個字符串的前一個位置,刪,則為當前字符串的當前位對應前一個字符串的前一個位置。為了增加一個增的位置,需要我們在其前面,所以我們在兩個字符串的開始處設置一增加的位置。

public class Solution {/*** @param word1 & word2: Two string.* @return: The minimum number of steps.*/public int minDistance(String word1, String word2) {// write your code hereif(word1==null||word2==null)return 0;int len1 = word1.length();int len2 = word2.length();if(len1==0||len2==0)return Max(len2,len1);int [][]dp = new int[len2+1][len1+1];for(int i=0; i<=len1; i++){dp[0][i]=i;}for(int i=0; i<=len2; i++){dp[i][0]=i;}for(int i=1; i<=len2; i++){for(int j=1; j<=len1; j++){if(word2.charAt(i-1)==word1.charAt(j-1)){//狀態轉化,分別別是刪,增,改dp[i][j] = Min(Min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]);}else{dp[i][j] = Min(Min(dp[i-1][j]+1,dp[i][j-1]+1),dp[i-1][j-1]+1);}}}return dp[len2][len1];}public int Max(int a,int b){return a>b?a:b; }public int Min(int a,int b){return a<b?a:b; } }

5.N皇后問題

n皇后問題是將n個皇后放置在n*n的棋盤上,皇后彼此之間不能相互攻擊。
給定一個整數n,返回所有不同的n皇后問題的解決方案。

對于n皇后的問題,下一個皇后的布局位置將與之前的所有王后布局有關,因此通過動態規劃,沒安置一個皇后就作為一個狀態,然后判斷之前的已經安放的所有皇后的狀態,確定是否可以按這一個皇后,通過遞歸的方式實現。

public int num1(int n){if(n<1)return 0;int[] record = new int[n];return process(0,record,n); }public int process(int i,int []record,int n){if(i==n)return 1;int res = 0;for(int j=0; j<n; j++){if(isValid(record,i,j)){record[i]=j;res+=process(i+1,record,n);}}return res; }public boolean isValid(int[]record,int i,int j){for(int k=0; k<i; k++){if(j==record[k]||Math.abs(record[k]-j)==Math.abs(i-k)){return false;}}return true; }

后記

對于動態規劃的更多問題,將會繼續更新,陸續也會寫一些貪心算法等常見的算法類型。


總結

以上是生活随笔為你收集整理的动态规划经典问题的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。