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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

动态规划——入门(1)

發布時間:2025/4/16 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划——入门(1) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

被動態算法折磨的我看到了大神的這篇文章,覺得明白了許多,轉載過來,以便回顧。然沒詢問大神意見,望諒解
大神使用C++寫的,我這代碼是java
原文地址為:https://blog.csdn.net/baidu_28312631/article/details/47418773

動態規劃相信大家都知道,動態規劃算法也是新手在剛接觸算法設計時很苦惱的問題,有時候覺得難以理解,但是真正理解之后,就會覺得動態規劃其實并沒有想象中那么難。網上也有很多關于講解動態規劃的文章,大多都是敘述概念,講解原理,讓人覺得晦澀難懂,即使一時間看懂了,發現當自己做題的時候又會覺得無所適從。我覺得,理解算法最重要的還是在于練習,只有通過自己練習,才可以更快地提升。話不多說,接下來,下面我就通過一個例子來一步一步講解動態規劃是怎樣使用的,只有知道怎樣使用,才能更好地理解,而不是一味地對概念和原理進行反復琢磨。
首先,我們看一下這道題(此題目來源于北大POJ):
數字三角形(POJ1163)

在上面的數字三角形中尋找一條從頂部到底邊的路徑,使得路徑上所經過的數字之和最大。路徑上的每一步都只能往左下或 右下走。只需要求出這個最大和即可,不必給出具體路徑。 三角形的行數大于1小于等于100,數字為 0 - 99
輸入格式:
5 //表示三角形的行數 接下來輸入三角形
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
要求輸出最大和
接下來,我們來分析一下解題思路:
首先,肯定得用二維數組來存放數字三角形
然后我們用D( r, j) 來表示第r行第 j 個數字(r,j從1開始算)
我們用MaxSum(r, j)表示從D(r,j)到底邊的各條路徑中,最佳路徑的數字之和。
因此,此題的最終問題就變成了求 MaxSum(1,1)
當我們看到這個題目的時候,首先想到的就是可以用簡單的遞歸來解題:
D(r, j)出發,下一步只能走D(r+1,j)或者D(r+1, j+1)。故對于N行的三角形,我們可以寫出如下的遞歸式

if(i==n-1) {Maxsun[i][j]=num[i][j];}else{Maxsun[i][j]=Math.max(MaxSum(i+1, j, num),MaxSum(i+1, j+1, num))+num[i][j];}

根據上面這個簡單的遞歸式,我們就可以很輕松地寫出完整的遞歸代碼:

package other_demo;import java.util.Scanner;public class dp {static int n;public static int MaxSum(int i,int j,int[][] num) {if(i==n-1) {return num[i][j];}return Math.max(MaxSum(i+1, j, num),MaxSum(i+1, j+1, num))+num[i][j];}public static void main(String[] args) {Scanner sc=new Scanner(System.in);n=sc.nextInt();int[][] num=new int[n][n];for (int i = 0; i < n; i++) {for (int j = 0; j <= i; j++) {num[i][j]=sc.nextInt();}}System.out.println("最大路徑為:"+MaxSum(0, 0,num));} }

但是上面這個代碼的運行時間會很長,因為我們重復計算了,當我們在進行遞歸時,計算機幫我們計算的過程如下圖:

就拿第三行數字1來說,當我們計算從第2行的數字3開始的MaxSum時會計算出從1開始的MaxSum,當我們計算從第二行的數字8開始的MaxSum的時候又會計算一次從1開始的MaxSum,也就是說有重復計算。這樣就浪費了大量的時間。也就是說如果采用遞規的方法,深度遍歷每條路徑,存在大量重復計算。則時間復雜度為 2的n次方,對于 n = 100 行,肯定超時。

接下來,我們就要考慮如何進行改進,我們自然而然就可以想到如果每算出一個MaxSum(r,j)就保存起來,下次用到其值的時候直接取用,則可免去重復計算。那么可以用n方的時間復雜度完成計算。因為三角形的數字總數是 n(n+1)/2。
根據這個思路,我們就可以將上面的代碼進行改進,使之成為記憶遞歸型的動態規劃程序:

package other_demo;import java.util.Scanner;public class dp {static int n;public static int MaxSum(int i,int j,int[][] num,int[][] maxnum) {if(maxnum[i][j]!=-1) {return maxnum[i][j];}if(i==n-1) {return num[i][j];}maxnum[i][j]=Math.max(MaxSum(i+1, j, num,maxnum),MaxSum(i+1, j+1, num,maxnum))+num[i][j];return maxnum[i][j];}public static void main(String[] args) {Scanner sc=new Scanner(System.in);n=sc.nextInt();int[][] num=new int[n][n];//使用maxsum[i][j]來記錄num[i][j]路徑一下的最大路徑值。int[][] maxnum=new int[n][n];for (int i = 0; i < n; i++) {for (int j = 0; j <= i; j++) {num[i][j]=sc.nextInt();maxnum[i][j]=-1;}}System.out.println("最大路徑為:"+MaxSum(0, 0,num,maxnum));}}

雖然在短時間內就AC了。但是,我們并不能滿足于這樣的代碼,因為遞歸總是需要使用大量堆棧上的空間,很容易造成棧溢出,我們現在就要考慮如何把遞歸轉換為遞推,讓我們一步一步來完成這個過程。

我們首先需要計算的是最后一行,因此可以把最后一行直接寫出,如下圖:
現在開始分析倒數第二行的每一個數,現分析數字2,2可以和最后一行4相加,也可以和最后一行的5相加,但是很顯然和5相加要更大一點,結果為7,我們此時就可以將7保存起來,然后分析數字7,7可以和最后一行的5相加,也可以和最后一行的2相加,很顯然和5相加更大,結果為12,因此我們將12保存起來。以此類推。。我們可以得到下面這張圖:

然后按同樣的道理分析倒數第三行和倒數第四行,最后分析第一行,我們可以依次得到如下結果:


上面的推導過程相信大家不難理解,理解之后我們就可以寫出如下的遞推型動態規劃程序:

package other_demo;import java.util.Scanner;public class dp { public static void main(String[] args) {Scanner sc=new Scanner(System.in);int n;n=sc.nextInt();int[][] num=new int[n][n];int[][] maxnum=new int[n][n];for (int i = 0; i < n; i++) {for (int j = 0; j <= i; j++) {num[i][j]=sc.nextInt();}}for (int j =0; j <n; j++) {maxnum[n-1][j]=num[n-1][j];}for (int i = n-2; i >=0; i--) {for (int j = 0; j <= i; j++) {maxnum[i][j]=Math.max(maxnum[i+1][j], maxnum[i+1][j+1])+num[i][j];}}System.out.println("最大路徑為:"+maxnum[0][0]);} }

我們的代碼僅僅是這樣就夠了嗎?當然不是,我們仍然可以繼續優化,而這個優化當然是對于空間進行優化,其實完全沒必要用二維maxSum數組存儲每一個MaxSum(r,j),只要從底層一行行向上遞推,那么只要一維數組maxSum[100]即可,即只要存儲一行的MaxSum值就可以。
對于空間優化后的具體遞推過程如下:






接下里的步驟就按上圖的過程一步一步推導就可以了。進一步考慮,我們甚至可以連maxSum數組都可以不要,直接用D的第n行直接替代maxSum即可。但是這里需要強調的是:雖然節省空間,但是時間復雜度還是不變的。
依照上面的方式,我們可以寫出如下代碼:

package other_demo;import java.util.Scanner;public class dp { public static void main(String[] args) {Scanner sc=new Scanner(System.in);int n;n=sc.nextInt();int[][] num=new int[n][n];int[][] maxnum=new int[n][n];for (int i = 0; i < n; i++) {for (int j = 0; j <= i; j++) {num[i][j]=sc.nextInt();}}for (int j =0; j <n; j++) {maxnum[n-1][j]=num[n-1][j];}for (int i = n-2; i >=0; i--) {for (int j = 0; j <= i; j++) {maxnum[n-1][j]=Math.max(maxnum[n-1][j], maxnum[n-1][j+1])+num[i][j];}}System.out.println("最大路徑為:"+maxnum[n-1][0]);} }

作者:ChrisYoung1314
來源:CSDN
原文:https://blog.csdn.net/baidu_28312631/article/details/47418773
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!

總結

以上是生活随笔為你收集整理的动态规划——入门(1)的全部內容,希望文章能夠幫你解決所遇到的問題。

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