A 數塔 (遞迴 記憶化陣列 DP)

2021-08-21 14:46:33 字數 2580 閱讀 3820

在講述dp演算法的時候,乙個經典的例子就是數塔問題,它是這樣描述的:

有如下所示的數塔,要求從頂層走到底層,若每一步只能走到相鄰的結點,則經過的結點的數字之和最大是多少?

已經告訴你了,這是個dp的題目,你能ac嗎?

輸入資料首先包括乙個整數c,表示測試例項的個數,每個測試例項的第一行是乙個整數n(1 <= n <= 100),表示數塔的高度,接下來用n行數字表示數塔,其中第i行有個i個整數,且所有的整數均在區間[0,99]內。

對於每個測試例項,輸出可能得到的最大和,每個例項的輸出佔一行。

sample input

157

3 88 1 0

2 7 4 4

4 5 2 6 5

sample output

30
1.遞迴

dp(i,j)來表示第i行第j列的數字走到最後一行的最大和,a[i][j]表示輸入的二維陣列,本題從a[i][j]開始,下一步只能a[i+1][j]或者a[i+1][j+1],此時dp(i,j)就是dp(i+1,j)+a[i][j]或者dp(i+1,j+1)+a[i][j],所以想知道往**走,就要知道dp(i+1,j)和dp(i+1,j+1)誰大,那個值更大,就向對應的位置走。

附源**(tle): 

#include #include #include #include #include using namespace std;

int a[105][105];

int n;

int dp(int i,int j)

int main()

return 0;

}

如果執行**,你就會發現當n的值很大的時候,程式就已經很慢了,這是因為每次計算dp(i,j)時,dp(i+1,j)和dp(i+1,j+1)都要被計算一次dp(1,1)被計算一次,從第2行開始每個位置被計算的次數就是

1 11 2 1

1 3 3 1

1 4 6 4 1

次數:dp(i,j)=dp(i-1,j)+dp(i-1,j-1)

上面可以得到從第1行開始每行的計算次數一次為1 2 4 8 16,總的計算次數2^0+2^1+...+2^n=2^n-1,由此可以看出上述方法的時間複雜度為o(2^n)

2.記憶化陣列

根據上述分析,用一中的方法去做這個題的話,結果肯定是tle哇。那麼有沒有辦法優化呢?能不能降低1中的計算次數呢?

我們可以將dp(i,j)計算的結果儲存在乙個二維陣列b[i][j],那麼下次用的時候是不是就可以直接從陣列中拿來用啦,這個dp(i,j)只需要計算一次,我們就可以完成計算了。開始講二維陣列中的數全部初始化為0,表示對應的位置還沒有被計算過。沒有計算過就將計算儲存在陣列中(此時要計算的是b[i+1][j+1],b[i+1][j]的結果,因為b[i][j]是由他們當中最大的那個值決定的)。

附源**(tle):

#include #include #include #include #include using namespace std;

int a[105][105];

int b[105][105];

int n;

int dp(int i,int j)

int main()

return 0;

}

由於每個位置只用計算一次,所以計算總次數:1+2+3+...+n=n*(n+1)/2,時間複雜度:o(n^2)

3.dp

遞迴的問題有的時候可以轉換成遞推的問題來做。從dp[i][j]逐步向上遞推,結合2中方法,每個位置對應的已知結果已經儲存在二維陣列dp[i][j]中,相當於打表,已知二維陣列的邊界都是0,有遞推公式:

dp[i][j]取決於它下面和它右下方的元素決定。

附源**(ac):

#include #include #include #include #include using namespace std;

int a[105][105];

int dp[105][105];

int main()

return 0;

}

時間複雜度:o(n^2)

4.dp(優化)

由遞推公式可知,dp[i][j]用來計算過dp[i-1][j]後就用不到了,所以可以將計算出來的dp[i-1][j]存在dp[i-1][j]的位置上。

dp[i-1][1]

dp[i-1][2]

dp[i-1][3]

dp[i-1][j]

所以dp[1][1]可以存放在dp[i][1]的位置上。綜上:一維陣列dp就可以解決問題了。

附源**(ac):

#include #include #include #include #include using namespace std;

int a[105][105];

int dp[105];

int main()

return 0;

}

時間複雜度:o(n^2)

自下而上:下面計算的結果一定能被上面用到。

洛谷P1216數塔(逆向遞推遞迴 記憶化,dp)

題目很簡單,是dp和記憶化搜尋的入門練手好題 有乙個坑點,全為0的時候,記憶化沒初始化為其它值的話,還是暴力遞迴絕對超時。所以記憶化時,根據題目要求分析,一般都初始化為 1 1 include 2 include 3 include 4 include 5 include 6 include 7 u...

遞迴方法 記憶化dp)

比如 w 30,1,0 w 30,1,0 既滿足條件1又滿足條件2 這種時候我們就按最上面的條件來算 所以答案為1 輸入測試樣例由多組測試資料組成。每組測試資料第一行輸入三個整數 a b c 20 a,b,c 20 如果a,b,c均為 1則退出程式 輸出輸出遞迴後的結果 樣例輸入 copy 1 1 ...

記憶化搜尋入門 數塔

time limit 1000ms memory limit 65536kb submit statistic discuss problem description 給定乙個由n行數字組成的數字三角形如下圖所示。試設計乙個演算法,計算出從三角形的頂至底的一條路徑,使該路徑經過的數字總和最大。對於給...