入門DP | 1:數字三角形問題

數字三角形問題

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

問題描述: 給定一個有n行數字組成的數字三角形. 試設計一個算法, 計算出從三角形的頂至底的一條路徑, 使該路徑經過的數字和最大.

算法設計: 對於給定的n行數字組成的三角形, 計算從三角形頂至底的路徑經過的數字和的最大值.

數據輸入: 第1行數字三角形的行數n, 1<=n<=100. 接下來n行是數字三角形各行中的數字. 所有數字在0~99之間.

結果輸出: 第1行中的數是計算出的最大值.

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

題意很好理解:在一個數字三角形中,從頂端到底端,選擇一條元素和最大的路徑。(每次只可選擇當前位置左下/右下的元素)下圖是對測試用例的圖解:

簡單粗暴的思考一下:若用暴力枚舉,從頂層到底層一共有 n-1 次選擇,每次供選擇的情況爲2種,那麼一共有 2^{n-1} 條路徑。一一計算和再選取最大值,時間複雜度爲 O(2^n) ,oh,糟糕的指數時間 ┭┮﹏┭┮。


1、分析

此題很明顯是一個典型的動態規劃問題,因爲其滿足動態規劃的兩大特性:

Ⅰ 最優子結構

從頂層到最底層的最優(和最大)路徑中,期間任意一個元素到最底層的路徑都是最優的。那麼我們可以從底層往上推,得出頂層的最優路徑。比如說,已知第二層的最優路徑,我們求頂層的最優路徑,就是選取第二層中3或8的更優路徑和 + 7。所以,我們可以將底層的最優路徑看作上一層的子問題,下一層的最優路徑已知,那麼上一層的最優路徑就很容易推出。

Ⅱ 重疊子問題

上面已經分析過,題中一共有 2^{n-1} 條路徑,且每條路徑中會有子問題重疊。比如:7-3-1-7-5 與 7-3-8-7-5 中,倒數第二層到底層的的子路徑 7-5是重疊的。這也是爲什麼要用動態規劃而不用分治法求解的根源。


2、建模

數據的存儲

圖中是一個規整的等腰三角形,那麼我們儲存在程序中應該採用二維數組的對應存儲。下圖顯示的是一個簡單的分支部分應該對應在數組中的儲存方式。我們將三角形每一行對應於數組的一維下標:i = 0..n-1,將每一列對應於數組的二維下標:j = 0..i。在數字三角形中每次路徑的選擇可以選擇左下/右下,對應在數組中,在元素 a[i][j] 處的選擇應該是 a[i+1][j] 或 a[i+1][j+1]

動態求解

已經分析過本題的最優子結構,由底層的最優解可以推出頂層的最優解,所以求解過程是自底向上的。採用一個二維數組 dp[i][j] 來儲存 a[i][j] 元素到最底層的最優路徑值。那麼很容易列出狀態轉移方程:

當 i > n-1 : dp[i][j] = a[i][j] + max(dp[i+1][j] , dp[i+1][j+1])

當 i = n - 1 (初始情況): dp[i][j] = a[i][j] 


3、代碼實現 

直接附上AC代碼 

#include <cstdio>
#include <algorithm>
using namespace std;
#define MAXN 105

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

    /* 處理輸入的數字三角形 */
    for (int i = 0; i < n; i++)
        for (int j = 0; j <= i; j++) {
            scanf("%d", &a[i][j]);
            dp[i][j] = a[i][j];  //初始化dp數組
        }

    /* 自底向上的求解過程 */
    for (int i = n - 2; i >= 0; i--)  //從 n-2 ~ 0 層
        for (int j = 0; j <= i; j++)
            dp[i][j] += max(dp[i + 1][j], dp[i + 1][j + 1]);  //選取下一層的最優解 + 本元素值
    printf("%d\n", dp[0][0]);  //輸出結果
}


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



end 

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

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

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

在這裏插入圖片描述

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