1. 程式人生 > 其它 >[DP專欄]來源型DP

[DP專欄]來源型DP

今天,我們來聊一聊來源型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,就是要有明確的來源,就能做出來。
我們這期就到這了,下期再見~