在講述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行數字組成的數字三角形如下圖所示。試設計乙個演算法,計算出從三角形的頂至底的一條路徑,使該路徑經過的數字總和最大。對於給...