[DP專欄]來源型DP
阿新 • • 發佈:2021-10-05
今天,我們來聊一聊來源型DP。
比如說,這題。
雖然說是IOI1994這樣好像很牛的題目,不過好像不難。
一、數字三角形
1.貪心
如果我們走貪心的話,我可以直接給你一個反例:
1
0 1
100 0 1
101 0 0 1
如果走貪心,我們會首先選擇大的那邊,然後走了4個1。
可是,最優走法是1->0->100->101,答案是202。
為什麼貪心會錯?因為他只會選擇區域性的最優,不會去想那一邊會不會有更大的數。
貪心的問題在於:目光太短淺。
2.搜尋
貪心不行的話……emm我們就爆搜。
可是!n到1000誒!你可以獲得若干個TLE,而且程式碼還比正解長。
最主要是,你不好剪枝。
3.DP
終於請來了我們的主角——DP。
首先我們設計狀態:\(f_{i,j}\)表示從第\(i\)行第\(j\)列為開頭,得到的最大值。
邊界:顯然,\(f_{N,i}=a_{N,i}\)。最後一行的每個數得到的最大值都是自己。
狀態轉移方程:\(f_{i,j}=\max(f_{i+1,j},f_{i+1,j+1})+a_{i,j}\)每個數都可以從下面兩個數來。
因為DP要求無後效性,所以我們從下往上計算。
最後答案就是\(f_{1,1}\)。
4.程式碼
#include<iostream> using namespace std; int a[1005][1005]; int f[1005][1005]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) cin>>a[i][j]; for(int i=n;i>=1;i--){ for(int j=1;j<=i;j++){ f[i][j]=max(f[i+1][j],f[i+1][j+1])+a[i][j]; } } cout<<f[1][1]<<endl; return 0; }
二、過河卒
題目
如果不管馬的阻擋,我們單純看這個走格子,像什麼?
是不是小學的時候數格子的數學題?
是不是有技巧?
是不是每一個位置是他左邊加上面?
這就是DP!
最開始\(f_{0,0}=1\),之後遍歷每一個點,如果他不是第一行就加上\(f_{i-1,j}\),不是第一列就加上\(f_{i,j-1}\)。
部分程式碼
dp[0][0]=1; for(int i=0;i<=n;i++){ for(int j=0;j<=m;j++){ if(d[i][j]==false){//判斷是否可以走 if(i){ dp[i][j]+=dp[i-1][j]; } if(j){ dp[i][j]+=dp[i][j-1]; } } } } cout<<dp[n][m]<<endl;
三、總結
來源型DP,就是要有明確的來源,就能做出來。
我們這期就到這了,下期再見~