動態規劃演算法小結(二)石子問題
阿新 • • 發佈:2019-01-31
我們來看個動態規劃的例子吧:
問題描述 在一條直線上有n堆石子,每堆有一定的數量,每次可以將兩堆相鄰的石子合併,合併後放在兩堆的中間位置,合併的費用為兩堆石子的總數。求把所有石子合併成一堆的最小花費。輸入格式 輸入第一行包含一個整數n,表示石子的堆數。接下來一行,包含n個整數,按順序給出每堆石子的大小 。輸出格式 輸出一個整數,表示合併的最小花費。樣例輸入5
1 2 3 4 5樣例輸出33
程式碼:
- import java.util.Scanner;
- publicclass Main {
- publicstaticvoid main(String[] args) {
- Scanner in = new
- int n = in.nextInt();
- int[] a = newint[1010];
- int[][] temp = newint[1010][1010];
- int[][] dp = newint[1010][1010];
- for (int i=1; i<=n; i++) {
- a[i] = in.nextInt();
- temp[i][i] = a[i];
- }
- for (int i=1; i<n; i++) {
- for(int j=i+1; j<=n; j++) {
- temp[i][j] = temp[i][j-1] + a[j];
- }
- }
- for (int r=2; r<=n; r++) {
- for (int i=1; i<=n-r+1; i++) {
- int j=i+r-1;
- dp[i][j] = Integer.MAX_VALUE;
- for
- if(dp[i][j] > dp[i][k] + dp[k+1][j])
- dp[i][j] = dp[i][k] + dp[k+1][j];
- }
- dp[i][j] += temp[i][j];
- }
- }
- System.out.println(dp[1][n]);
- }
- }
演算法書上把解動態規劃問題分為四步:
(1)找出最優解性質
(2)遞迴定義最優解
(3)以自底向上的方式算出最優解
(4)根據計算最優值時得到的資訊構造最優解
第一步是確定該問題是否有最優子結構,石子問題中我們可以這樣分析,最後一步是將最後的兩堆石子a和b合併起來成為c,那我們就可以想到c的花費最小時,a、b是否也是取最小呢?顯然,如果不是那意味著c也可以取更小的值。所以石子問題的最優解包含其子問題的最優解。
第二步是建立遞迴關係。為了方便我們可以使用dp[i][j]表示第i堆石子到第j堆石子的最優值,我們要建立dp[i][j]與其子問題的遞迴關係:
顯然i=j時,石子沒有合併,就沒有花費,dp[i][j]=0;
i<j時,我們用k(k>=i&&k<=j)把dp[i][j]分成dp[i][k]與dp[k+1][j]兩個部分,那麼dp[i][j]=dp[i][k]+dp[k+1][j]+temp[i][j]//需要另一個數組temp[i][j]儲存第i堆石子到第j堆石子的石子總數
第三步根據第二步的遞迴思路計算dp[1][n],底層i/j從1/2遍歷到n-1/n,儲存到dp後,再往上1/3遍歷到n-2/n...一直到最後1/n,每一個i/j都需要用k(k>=i&&k<=j)分成兩部分,通過比較k取不同值時的dp[i][j]得到最小花費。
第四步構造最優解,這個問題的最優解就是最後a[1][n]的值啦。對第四步有疑問0-1揹包問題會有說明。