例題
[p1063 能量項鍊](
[p3205 [hnoi2010]合唱隊](
區間dp,顧名思義,在區間上dp,大多數題目的狀態都是由區間(類似於dp[l][r]這種形式)構成的,就是我們可以把大區間轉化成小區間來處理,然後對小區間處理後再回溯的求出大區間的值,主要的方法有兩種,記憶化搜尋和遞推。
memset
(dp,0,
sizeof
(dp)
)//初始dp陣列
for(
int len=
2;len<=n;len++
)}
for
(int len=
2;len<=n;len++)}
}}
我們定義f[i][j]為賣掉i到j之間的臨時得到的最大收益。
另外對與臨時的**我們做乙個字首和。
轉移方程就應該是f[i][j]=max(f[i][j-1],f[i+1][j])+dis[j]-dis[i-1]f[i][j]=max(f[i][j−1],f[i+1][j])+dis[j]−dis[i−1]貌似跟樓下的不是很一樣。
這是什麼意思呢
既然是買點i到j之間的,那麼這一天賣掉的一定是i或j,同時因為多了一天,所以我之前賣的應該滯後一天賣,也就是說每乙個物品再增加乙個單價,同時加上我現在賣出去的i或j,去乙個較大值就可以了。
#include
using
namespace std;
int dp[
2010][
2010];
//dp陣列
int a[
2010
],ans;
//輸入陣列
intmain()
for(
int i=
1;i<=n;i++
) ans=
max(ans,dp[n]
[i])
;//從最終狀態中取乙個最大值
cout
}
用f[l][r]表示以a[l]開頭a[r]結尾的數串的最大和,如k為i,j之間任一節點,有f[l][r]=max(f[l][r],f[l][k]+f[k][r]+a[l]*a[k]*a[r]); 對l,r的定義自己一定要十分清晰,從而確定好迴圈的邊界。
本題的小技巧:在環形問題中,可以選擇(i+1)%n的方式,但也可以將n個元素複製一遍,變成2*n個元素,簡化**。
但也有問題隨之而來,在更新時要將2*n個元素都更新,而不能只更新到前n個,否則訪問到n+1~2n時會出錯
#include
using
namespace std;
int f[
405]
[405];
int n,a[
205]
;int res;
intmain()
for(
int i=
2;i<=n+
1;i++)}
for(
int i=
1;i<=n;i++
) res=
max(res,f[i]
[n+i]);
cout
}
那麼我們要怎麼設計狀態,我們想,每給人進入隊伍裡,只有2種可能,1種是從左邊加入,另外1種是從右邊進入,所以我們的裝態是有3個數
f[i][j][0]表示的是第i人從左邊進來的方案數
f[i][j][1]表示的是第j人從右邊進來的方案數
從左邊進來肯定前1個人比他高,前1個人有2種情況,要麼在i+1號位置,要麼在j號位置。
同理,從右邊進來肯定前1個人比他矮,前1個人有2種情況,要麼在j-1號位置,要麼在i號位置。
#include
using
namespace std;
const
int mod=
19650827
;int f[
2010][
2010][
2],a[2010];
intmain()
cout<<
(f[1
][n][0
]+f[1]
[n][1]
)%mod;
return0;
}
蒟蒻 區間dp學習總結
例題 p1063 能量項鍊 p3205 hnoi2010 合唱隊 區間dp,顧名思義,在區間上dp,大多數題目的狀態都是由區間 類似於dp l r 這種形式 構成的,就是我們可以把大區間轉化成小區間來處理,然後對小區間處理後再回溯的求出大區間的值,主要的方法有兩種,記憶化搜尋和遞推。memset d...
蒟蒻 樹形dp學習總結
樹形dp,即基於樹的結構進行dp 一般來說樹形dp在設狀態轉移方程時都可以用f i 表示i這顆子樹怎麼怎麼樣的最優解,實現時一般都是用子樹更新父親 即從下向上更新 那麼首先應該考慮的是乙個乙個子樹的更新父親還是把所有子樹都算完了在更新父親?這就要因題而異了,一般來說有兩種情況 1.需要把所有子樹的資...
蒟蒻 字典樹學習總結
又稱單詞查詢樹,trie樹,是一種樹形結構,是一種雜湊樹的變種。典型應用是用於統計,排序和儲存大量的字串 但不僅限於字串 所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是 利用字串的公共字首來減少查詢時間,最大限度地減少無謂的字串比較,查詢效率比雜湊樹高。根節點不包含字元,除根節點外每乙個節點都只...