個人對簡單的dp問題的理解:找是否有重疊問題,明確遞推關系,怎么推的(順序千萬不要搞錯),找到狀態方程,循環時注意邊界條件和方程式是否嚴格成立。
轉自:https://www.cnblogs.com/zyx1301691180/p/5727918.html
HDU 2084
有如下所示的數塔,要求從頂層走到底層,若每一步只能走到相鄰的結點,則經過的結點的數字之和最大是多少?

已經告訴你了,這是個DP的題目,你能AC嗎?
- 刻畫一個最優解的結構特征。
- 遞歸地定義最優解的值。
- 計算最優解的值,通常采用自底向上的方法。
- 利用計算出的信息構造一個最優解。”
我覺得這道題的解題步驟就完全可以用這個來描述:
1.首先,一個最優解的結構特征,即從頂層走到底層,其各個節點的最大數字之和的為最優解。
2.題目中要求走法為自頂向下,且每一步只能走到相鄰的節點,那么每個節點只有兩種選擇,即這個節點的兩個子節點;
那么這個節點的最優解就等於這個節點的值加上其兩個節點的最優解的最大值;
3.自底向上的求解。
4.我們所求出來的解就是我們所需要的答案,則第四步在這里可以忽略掉。
最后附上我的代碼:
我用數組a來接收數塔的值,則第三部可以表示為:a[i][j]的最優解=max(a[i+1][j]的最優解,a[i+1][j+1]的最優解);
而數塔最底層節點的最優解就等於他本身的值,因為他只有一個;那么最底層的值已知,我們只需要從下往上遞推就可以了
即這三行代碼塊:
for(i=n-1;i>=0;i--) for(j=0;j<=i;j++) a[i][j]=a[i][j]+max(a[i+1][j],a[i+1][j+1]);
1 #include <bits/stdc++.h> 2 using namespace std ; 3 int main () 4 { 5 long long dp[105][105]; 6 int T; cin>>T; 7 while (T--) 8 { 9 int n; cin>>n; 10 memset(dp,0,sizeof(dp)); 11 for(int i=0; i<n; i++) 12 for(int j=0; j<=i; j++) 13 cin>>dp[i][j]; 14 15 for(int i=n-1; i>=0; i--) 16 for(int j=0; j<=i; j++) 17 dp[i][j]+=max(dp[i+1][j],dp[i+1][j+1]); 18 19 cout<<dp[0][0]<<endl ; 20 } 21 return 0; 22 }
//用滾動數組優化一下 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <string> using namespace std; int n; const int INF=0x3f3f3f3f; const int maxn=400; int Map[maxn][maxn], f[maxn]; int main() { //freopen("in.txt", "r", stdin); cin>>n; for(int i=1; i<=n; i++) for(int j=1; j<=i; j++) cin>>Map[i][j]; int ans=-INF; for(int i=1; i<=n; i++) for(int j=i; j>0; j--) { f[j]=max(f[j], f[j-1])+Map[i][j]; if(i==n) ans=max(ans, f[j]); } cout<<ans<<endl; return 0; }
很相似的一道題目,但搞了好久。
請保證Chair消滅最多敵人並且沖出包圍圈。假設Chair在左上角,達到右下角時算突圍成功。
4 1 2 3 4 5 6 9 8 9 1 0 1 2 3 4 5
值得注意的是,Chair是如此英勇,應當一往無前,也就是說只能向下或向右走。
圖示應為最大戰果——於是你應該輸出他們的和:35
1 #include <bits/stdc++.h> 2 using namespace std ; 3 int main () 4 { 5 long long dp[105][105]; 6 //int T; cin>>T; 7 //while (T--) 8 { 9 int n; cin>>n; 10 memset(dp,0,sizeof(dp)); 11 for(int i=0; i<n; i++) 12 for(int j=0; j<n; j++) 13 cin>>dp[i][j]; 14 /* 15 for(int i=n-1; i>=0; i--) 16 for(int j=0; j<n; j++) 17 dp[i][j]+=max(dp[i+1][j],dp[i][j+1]); 18 */ //錯解,重疊子結構順序弄反了; 19 20 for(int i=n-1; i>=0; i--) 21 for(int j=n-1; j>=0; j--) 22 dp[i][j]+=max(dp[i+1][j],dp[i][j+1]); 23 24 /* 25 for(int i=0; i<n; i++) 26 { 27 for(int j=0; j<n; j++) 28 cout<<dp[i][j]<<" "; 29 cout<<endl; 30 } 31 */ 32 cout<<dp[0][0]<<endl; 33 34 } 35 return 0; 36 }
今天又遇到一個很相似的題目,a了好久,發現自己跟個智障一樣。😂
HDU 2571
yifenfei一開始在矩形的左上角,目的當然是到達右下角的大魔王所在地。迷宮的每一個格子都受到幸運女神眷戀或者痛苦魔王的詛咒,所以每個格子都對應一個值,走到那里便自動得到了對應的值。
現在規定yifenfei只能向右或者向下走,向下一次只能走一格。但是如果向右走,則每次可以走一格或者走到該行的列數是當前所在列數倍數的格子,即:如果當前格子是(x,y),下一步可以是(x+1,y),(x,y+1)或者(x,y*k) 其中k>1。
為了能夠最大把握的消滅魔王lemon,yifenfei希望能夠在這個命運大迷宮中得到最大的幸運值。
每組測試數據的第一行是兩個整數n,m,分別表示行數和列數(1<=n<=20,10<=m<=1000);
接着是n行數據,每行包含m個整數,表示n行m列的格子對應的幸運值K ( |k|<100 )。
1 3 8 9 10 10 10 10 -10 10 10 10 -11 -1 0 2 11 10 -20 -11 -11 10 11 2 10 -10 -10
52
和上一題不一樣的地方是:1 走法改變了,2 數據有了負數(要么格式化的時候注意,要么寫狀態轉移方程的時候注意)
小結:提交之前要把自己調試寫的東西給全注釋掉,這次老是PE,很sb;
寫循環的時候尤其要注意循環的條件,不然都不知道錯那了;
對每一題的動態規划的過程要清楚的理解(務必非常清晰);
1 #include<bits/stdc++.h> 2 using namespace std ; 3 int main() 4 { 5 int T; cin>>T; 6 while(T--) 7 { 8 int a[25][1005]={0}; 9 int n,m; cin>>n>>m; 10 for(int i=1; i<=n; i++) 11 for(int j=1; j<=m; j++) 12 cin>>a[i][j]; 13 14 for(int j=m; j>0; j--) 15 a[n][j]+=a[n][j+1]; 16 17 for(int i=n-1; i>0; i--) 18 for(int j=m; j>0; j--) 19 { 20 int maxn=a[i][j+1]; 21 if(j==m) 22 maxn=a[i+1][j]; 23 for(int k=2; j*k<m; k++) 24 maxn=max(a[i][k*j],maxn); 25 a[i][j]+=max(a[i+1][j],maxn); 26 } 27 28 /* 29 for(int i=1; i<=n; i++) 30 { 31 for(int j=1; j<=m; j++) 32 cout<<a[i][j]<<" "; 33 cout<<endl; 34 } 35 */ 36 37 cout<<a[1][1]<<endl; 38 39 } 40 return 0; 41 }