動態規划之數字三角形(三種解法:遞歸,遞推,記憶化搜索)


Description

 

如圖所示,有一個群島,共分為若干層,第1層有一個島嶼,第2層有2個島嶼,......,第n層有n個島嶼。每個島上都有一塊寶,其價值是一個正整數(圖中圓圈中的整數)。尋寶者只允許從第一層的島嶼進入,從第n層的島嶼退出,不能后退,他能收集他所經過的所有島嶼上的寶貝。但是,從第i層的島嶼進入第i+1層的島嶼時,有且僅有有2條路徑。你的任務是:對於給定的群島和島上寶貝的價值,計算一個拾寶者行走一趟所能收集寶貝的最大價值。

 

Input

第一行是一個整數n,在[2,20]之間,表示要輸入的案例的數量。后面緊跟n個案例。對每個案例,第一行是一個整數mi,在[2, 50]之間,表示該案例中島嶼的層數,后面緊跟mi*(mi+1)/2行,每行有1個整數,表示響應的島嶼上寶貝的價值,每個數值的范圍是[1, 1000]。注意:如果mi是4,而后面緊跟的10行的元素分別是5, 2, 1, 4, 7, 9, 4, 2, 3, 5,則表示第一層的數據是5;第2層的數據是2, 1;第3層的數據是4, 7, 9;第4層的數據是4, 2, 3, 5。

Output

對每一個案例,用一行輸出一個整數,表示輸出拾寶者所能收集寶貝的最大價值。

Sample Input

2
4
5
2
1
4
7
9
2
2
3
5
2
1
2
3

Sample Output

20
4

方法:動態規划。把當前位置(i,j)看成一個狀態,設指標函數d(i,j)為從格子(i,j)出發時能得到的最大和,原問題的解為d(1,1)。容易得出狀態轉移方程d(i,j) = a(i,j) + max{d(i+1,j), d(i+1, j+1)}

方法一:遞歸計算(時間復雜度O(2^n),會超時)

 1 #include <cstdio>
 2 #include <algorithm>    //for max(val1,val2)
 3 #include <iostream>
 4 using namespace std;
 5 int n;
 6 int a[51][51];
 7 int main()
 8 {
 9     int t;
10     scanf("%d", &t);
11     while (t--)
12     {
13         int i,j;
14         scanf("%d",&n);
15         for(i=1;i<=n;i++)
16           for(j=1;j<=i;j++)
17             scanf("%d",&a[i][j]);
18         int d(int, int);
19         printf("%d\n", d(1, 1));
20 }
21     system("pause");
22     return 0;
23 }
24 
25 int d(int i, int j)
26 {
27     if (i == n)
28        return a[i][j] + 0;
29     else
30        return a[i][j] + max(d(i+1, j), d(i+1, j+1));
31 }

法2:遞推計算(時間復雜度O(n^2),需要輔助數組d)

 1 #include <iostream>
 2 #include <algorithm>                //for max(val1, val2)
 3 #include <cstdio>
 4 using namespace std;
 5 int n;
 6 int a[51][51];
 7 int d[51][51];
 8 int main()
 9 {
10     int t;
11     scanf("%d", &t);
12     while (t--)
13     {
14         int i, j;
15         scanf("%d", &n);
16         for (i = 1; i <= n; ++i)
17             for (j = 1; j <= i; ++j)
18                 scanf("%d", &a[i][j]);
19         for (i = 1; i <= n; ++i)
20             d[n][i] = a[n][i];          //邊界處理
21         for (i = n - 1; i >= 1; --i)  //逆序計算
22             for (j = 1; j <= i; ++j)
23                 d[i][j] = a[i][j] + max(d[i+1][j], d[i+1][j+1]);
24         printf("%d\n", d[1][1]);
25     }
26     system("pause");
27     return 0;
28 }

法3:記憶化搜索(遞歸函數,O(n^2))

 1 #include <iostream>
 2 #include <algorithm>            //for max(val1, val2)
 3 #include <cstdio>
 4 #include <cstring>              //for *memset(s, c, n)   
 5 using namespace std;
 6 int n;
 7 int a[51][51];
 8 int d[51][51];
 9 int main()
10 {
11     int t;
12     scanf("%d", &t);
13     while (t--)
14     {
15         memset(d, -1, sizeof(d));     //初始化數組d為-1 
16         int i, j;
17         scanf("%d", &n);
18         for (i = 1; i <= n; ++i)
19             for (j = 1; j <= i; ++j)
20                 scanf("%d", &a[i][j]);
21         int f(int, int);
22         printf("%d\n", f(1, 1));
23     }
24     system("pause");
25     return 0;
26 }
27 
28 int f(int i, int j)
29 {
30     if (d[i][j] >= 0)     //由於每個數范圍為[1,1000],d[i][j]初始化為-1,可以根據d[i][j] >= 0判斷是否已經被計算過
31         return d[i][j];
32     if (i == n)
33         return a[i][j] + 0;
34     else
35         return d[i][j] = a[i][j] + max(f(i+1,j), f(i+1, j+1)); //保存d[i][j]並返回
36 }

本菜花了幾個小時整理的,在devc下編譯通過。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM