入門DP | 2:租用遊艇問題

租用遊艇問題

成績 10 開啓時間 2020年03月24日 星期二 23:15
折扣 0.8 折扣時間 2020年04月21日 星期二 23:55
允許遲交 關閉時間 2020年04月21日 星期二 23:55

問題描述: 長江遊艇俱樂部在長江上設置了n個遊艇出租站1,2,…,n. 遊客可在這些遊艇出租站租用遊艇, 並在下游的任何一個遊艇出租站歸還遊艇. 遊艇出租站i到出租站j之間的租金爲r(i,j), 1<=i<j<=n. 試設計一個算法, 計算出從遊艇出租站1到遊艇出租站n所需的最少租金, 並分析算法的計算複雜性.

算法設計: 對於給定的遊艇出租站i到遊艇出租站j的租金r(i,j), 1<=i<j<=n. 計算出租站1到n所需的最少租金.

數據輸入: 第1行有一個正整數n, n<=200, 表示有n個遊艇出租站. 接下來n-1行是r(i,j), 1<=i<j<=n.

結果輸出: 遊艇出租站1到n最少租金.

  測試輸入 期待的輸出 時間限制 內存限制 額外進程
測試用例 1  
  1. 3↵
  2. 5 15↵
  3. 7↵
 
  1. 12↵
1秒 64M 0

 本題可以說是區間DP | 1:矩陣鏈乘問題(含優化) —— 例題:矩陣鏈乘、合併石子的一個小小部分(看完這一題再去看鏈接就豁然開朗~)。不過我是A完了那一題再來些這個的,就是一個入門級的DP,小菜一碟啦~

處理輸入

一個有n個節點組成的線段,給出了任意兩不同節點之間的收費r。對節點分別編號爲 1..n,可以用二維數組 r[ i ][ j ] 表示節點 i 到節點 j 的收費r。

動態分析

其實對於一個長度爲 l 的線段 r[ i ][ j ] (其中有j = i + l),它可以由兩段長度小於 l 的子線段組成,且若要原線段是最優解(花費爲最小值),其兩個子線段也應該是最優解。故此模型滿足最優子結構的性質,且容易看出,很多子結構是重疊的,此模型又具備重疊子問題的性質。故,本題採用動態規劃求解啦~

模型建立

採用一個二維數組 dp[ i ][ j ] 記錄節點 i 到節點 j 的最優解(最小收費r)。根據上面對最優子結構分析,我們應該先求出子問題的最優解再推斷出原問題的最優解,故可以使用自底向上的方法來實現。先從 l = 2 開始分析,依次上推到 l = n即得到答案 dp[1][n]。狀態轉移方程如下:

dp[i][j] = min_{i< k< j}(dp[i][k]+dp[k][j])

不要忽略了節點  i 到節點 j 不劃分子問題的情況,直接取r[ i ][ j ]。爲方便起見,我們將dp[ i ][ j ]先賦值爲r[ i ][ j ]即可,之後在比較的過程中會選取最小的。


完整AC代碼

#include <cstdio>
#include <algorithm>

#define MAXN 205
using namespace std;

int main() {
    int n, r[MAXN][MAXN], dp[MAXN][MAXN];
    scanf("%d", &n);

    /* 處理輸入 */
    for (int i = 1; i < n; i++)
        for (int j = i + 1; j <= n; j++)
            scanf("%d", &r[i][j]);

    /* 自底向上求解 */
    for (int l = 1; l < n; l++) {  //長度l:1..n
        for (int i = 1; i <= n - l; i++) {  //遍歷所有長度爲l的情況
            int j = i + l;  
            dp[i][j] = r[i][j];  //賦予初值(不斷開的情況)
            for (int k = i + 1; k < j; k++)  //尋找中間點斷開,更新最優的
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
        }
    }
    printf("%d\n", dp[1][n]);
}


有任何問題歡迎評論交流,如果本文對您有幫助不妨點點贊,嘻嘻~  



end 

歡迎關注個人公衆號 雞翅編程 ”,這裏是認真且乖巧的碼農一枚。

---- 做最乖巧的博客er,做最紮實的程序員 ----

旨在用心寫好每一篇文章,平常會把筆記彙總成推送更新~

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章