數字三角形問題
成績 | 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秒 | 64M | 0 |
題意很好理解:在一個數字三角形中,從頂端到底端,選擇一條元素和最大的路徑。(每次只可選擇當前位置左下/右下的元素)下圖是對測試用例的圖解:
簡單粗暴的思考一下:若用暴力枚舉,從頂層到底層一共有 n-1 次選擇,每次供選擇的情況爲2種,那麼一共有 條路徑。一一計算和再選取最大值,時間複雜度爲 ,oh,糟糕的指數時間 ┭┮﹏┭┮。
1、分析
此題很明顯是一個典型的動態規劃問題,因爲其滿足動態規劃的兩大特性:
Ⅰ 最優子結構
從頂層到最底層的最優(和最大)路徑中,期間任意一個元素到最底層的路徑都是最優的。那麼我們可以從底層往上推,得出頂層的最優路徑。比如說,已知第二層的最優路徑,我們求頂層的最優路徑,就是選取第二層中3或8的更優路徑和 + 7。所以,我們可以將底層的最優路徑看作上一層的子問題,下一層的最優路徑已知,那麼上一層的最優路徑就很容易推出。
Ⅱ 重疊子問題
上面已經分析過,題中一共有 條路徑,且每條路徑中會有子問題重疊。比如: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 :
當 i = n - 1 (初始情況):
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,做最紮實的程序員 ----
旨在用心寫好每一篇文章,平常會把筆記彙總成推送更新~