DP專題(蒟蒻版)——初學者
DP真是難,這該怎麼辦???
dp,每個OIer都要經歷的一大關卡,其中,dp的進階:狀壓dp,樹形dp等等就算是省選選手也會頭疼好一陣子
我們今天只會介紹基礎dp
1、基礎dp
基礎dp,即線性dp,也就是可以用類似ans+=min(ans,xxxxxxx)等的dp格式,這種dp思考起來比較簡單
我們從例題入手,開始講解:
1、數字三角形ioi94
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
上圖給出了一個數字三角形。從三角形的頂部到底部有很多條不同的路徑。對於每條路徑,把路徑上面的數加起來可以得到一個和,你的任務就是找到最大的和。
注意:路徑上的每一步只能從一個數走到下一層上和它最近的左邊的那個數或者右邊的那個數
輸入
輸入的是一行是一個整數N (1 < N <= 100),給出三角形的行數。下面的N行給出數字三角形。數字三角形上的數的範圍都在0和100之間。
輸出
輸出最大的和。格式: max=最大值
樣例輸入[複製]
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
樣例輸出[複製]
max=30
這道題用搜索來做是一種方法,但搜尋不能AC,會TLE(所以搜尋不能滿足某些大佬想要AK IOI的需求)於是這裡我們來講一下如何用dp來做
【思路】
dp要滿足的一個重要的點是無後效性,只要無後效性時,多半可以用dp(或貪心)來做,這裡,對於每一個點,從上往下(或從下往上)來執行時,不會對下面(或上面)的點造成影響,所以,我們可以考慮,對於每一行中的點,我們可以考慮計算這一行中所有點的最大值,即是:
5 a[1][1] 3 2 a[2][1],a[2][2] 1 4 2 a[3][1],a[3][2],a[3][3]; 對於這組例子,我們可以將它分解為 5 5 3 3 2 2 3 2 1 4 2 這5個子問題,對於每個子問題,我們都可以很輕鬆的由上面的值來算出這個三角形的最大值 所以會有: f[2][1]=a[2][1]+f[1][1]; f[2][2]=a[2][2]+f[1][1]; f[3][1]=a[3][1]+f[2][1]; f[3][1]=a[3][1]+max(f[2][1],f[2][2]); f[2][1]3=a[2][1]+f[1][1]; 只需要將這幾個狀態轉移方程化成一般形式扔到for中即可 P.S:從下往上算不用計算邊界 程式碼: #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; int a[1001][1001],f[1001][1001],n; int main(){ cin>>n; for(int i=1;i<=n;i++) for(int j=1;j<=i;j++) cin>>a[i][j]; f[1][1]=a[1][1]; for(int i=2;i<=n;i++){ for(int j=1;j<=i;j++){ f[i][j]=a[i][j]+max(f[i-1][j-1],f[i-1][j]); } } for(int i=1;i<=n;i++){ if(f[n][1]<f[n][i]) f[n][1]=f[n][i]; } cout<<"max="<<f[n][1]; return 0; }
【練習】:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
上圖給出了一個數字三角形。從三角形的頂部到底部有很多條不同的路徑。對於每條路徑,把路徑上面的數加起來可以得到一個和,你的任務就是找到最大的和。
注意:路徑上的每一步只能從一個數走到下一層上和它最近的左邊的那個數或者右邊的那個數,
當然這道題不是這樣,加上1個條件了,某一步可以隨意跳
輸入
輸入的是一行是一個整數N (1 < N <= 100),給出三角形的行數。
下面的N行給出數字三角形。數字三角形上的數的範圍都在0和100之間。
輸出
輸出最大的和。格式: max=最大值
樣例輸入[複製]
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
樣例輸出[複製]
max=35
【標程】
請自己敲完後再看!!!
#include<iostream>
using namespace std;
int a[101][101],f[101][101][101];
int main()
{
int n,maxn=0;
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
cin>>a[i][j];
f[i][j][0]=max(f[i-1][j][0],f[i-1][j-1][0])+a[i][j];
f[i][j][1]=max(f[i-1][j][1],f[i-1][j-1][1])+a[i][j];
for(int k=1;k<=i;k++){
f[i][j][1]=max(f[i-1][k][0]+a[i][j],f[i][j][1]);
}
}
for(int i=1;i<=n;i++)
{
if(maxn<=f[n][i][1])
maxn=f[n][i][1];
}
}
cout<<"max="<<maxn;
return 0;
}