區間dp(模板 例題)

2021-08-20 20:58:26 字數 4113 閱讀 5960

參考博文:區間dp小結(附經典例題)

首先,什麼是區間dp?它是幹什麼的?

先在小區間進行dp得到最優解,然後再利用小區間的最優解合併求大區間的最優解

操作往往涉及到區間合併問題

以上。

模板如下:

//mst(dp,0) 初始化dp陣列  

for(int i=1;i<=n;i++)

for(int

len=2;len

<=n;len++) //區間長度

for(int i=1;i<=n;i++) //列舉起點

}

注意區間的列舉起點。

例題1:51nod1021 石子合併

題意:

n堆石子擺成一條線。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的代價。計算將n堆石子合併成一堆的最小代價。

首先,假如合併的次序沒有限制,那麼我們將每堆石子看成乙個葉結點,每次合併的代價為數中結點,採用貪心的策略,則整個合併過程就是乙個哈夫曼樹的建樹過程。

但是這裡合併的時候要求每次只能合併相鄰的兩堆,則貪心這裡就會出錯了,因此我們採用dp的思想進行求解。

我們用dp[i][j]來表示合併第i堆到第j堆石子的最小代價,那麼狀態轉移方程為:

dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+w[i][j]);

其中w[i][j]表示把兩部分合併起來的代價,即從第i堆到第j堆石子個數的和,為了方便查詢,我們可以用sum[i]表示從第1堆到第i堆的石子個數和,那麼w[i][j]=sum[j]-sum[i-1].

**如下:

#include 

#include

#include

#include

using

namespace

std;

#define mst(a,b) memset((a),(b),sizeof(a))

#define rush() int t;scanf("%d",&t);while(t--)

typedef

long

long ll;

const

int maxn = 205;

const ll mod = 1e9+7;

const ll inf = 1e18;

const

double eps = 1e-9;

int n,x;

int sum[maxn];

int dp[maxn][maxn];

int main()

for(int len=2;len<=n;len++)

for(int i=1;i<=n;i++)

} printf("%d\n",dp[1][n]);

} return

0;

}

當然有的時候直接這樣裸著o(n^3)會t,所以我們有平行四邊形優化版本:

由於狀態轉移時是三重迴圈的,我們想能否把其中一層優化呢?尤其是列舉分割點的那個,顯然我們用了大量的時間去尋找這個最優分割點,所以我們考慮把這個點找到後儲存下來

用s[i][j]表示區間[i,j]中的最優分割點,那麼第三重迴圈可以從[i,j-1)優化到【s[i][j-1],s[i+1][j]】。(這個時候小區間s[i][j-1]和s[i+1][j]的值已經求出來了,然後通過這個迴圈又可以得到s[i][j]的值)。

**如下:

#include 

#include

#include

#include

using

namespace

std;

#define mst(a,b) memset((a),(b),sizeof(a))

#define rush() int t;scanf("%d",&t);while(t--)

typedef

long

long ll;

const

int maxn = 205;

const ll mod = 1e9+7;

const ll inf = 1e18;

const

double eps = 1e-9;

int n,x;

int sum[maxn];

int dp[maxn][maxn];

int s[maxn][maxn];

int main()

for(int len=2;len<=n;len++)

for(int i=1;i<=n;i++)

} }

printf("%d\n",dp[1][n]);

} return

0;

}

例題2:hdu3506 猴子派對

題意:

問題轉化後其實就是環形石子合併,即現在有圍成一圈的若干堆石子,其他條件跟其那面那題相同,問合併所需最小代價。

解法:

把前n-1堆石子乙個個移到第n個後面,那樣環就變成了線,即現在有2*n-1堆石子需要合併,我們只要求下面的式子即可。求法與上面那題完全一樣。

這道題在uestc版本上不做平行四邊形優化會t。

#include

#include

#include

using

namespace

std;

typedef

long

long ll;

const

int inf = 99999999;

const

int maxn = 10000;

int a[maxn];

int sum[maxn], dp[maxn][maxn], s[maxn][maxn];

int main()

for (int i = 1; i <= 2 * n; i++)

sum[i] = sum[i - 1] + a[i];

for (int m = 1; m <= n; ++m)

else }}

}}

int ans = inf;

for (int i = 1; i <= n; ++i)

printf("%d\n", ans);

}return

0;}

例題3:hdu5115 dire wolf

題意:

有一排狼,每只狼有乙個傷害a,還有乙個傷害b。殺死乙隻狼的時候,會受到這只狼的傷害a和這只狼兩邊的狼的傷害b的和。如果某位置的狼被殺,那麼殺它左邊的狼時就會收到來自右邊狼的b,因為這兩隻狼是相鄰的了。求殺掉一排狼的最小代價。

解法:

設dp[i][j]為消滅編號從i到j只狼的代價,那麼結果就是dp[1][n],列舉k作為最後乙隻被殺死的狼,此時會受到a[k]和b[i-1] b[j+1]的傷害,取最小的即可

可列出轉移方程:

dp[i][j]=min(dp[i][j], dp[i][k-1]+dp[k+1][j]+a[k]+b[i-1]+b[j+1]);

dp[i][i]=a[i]+b[i-1]+b[i+1];

**如下:

#include

#include

#include

#include

#include

using

namespace

std;

const

int n=220

const

int inf=0xfffffff

int main()

for(int l=0; l<=n; l++)//注意邊界}}

printf("case #%d: %d\n", t++, dp[1][n]);

}return

0;}

區間dp模板

板子題 n堆石子擺成一條線。現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆石子合併成新的一堆,並將新的一堆石子數記為該次合併的代價。計算將n堆石子合併成一堆的最小代價。例如 1 2 3 4,有不少合併方法 1 2 3 4 3 3 4 3 6 4 9 10 19 1 2 3 4 1 5 4 5...

理解區間dp和例題

一 講解 1 作用 用於不知道從 合併的動態規劃題,不比線性dp 2 解法步驟 即列舉區間長度,再列舉左端點,之後列舉區間的斷點進行轉移。3 核心思路 既然讓我求解在乙個區間上的最優解,那麼我把這個區間分割成乙個個小區間,求解每個小區間的最優解,再合併小區間得到大區間即可。所以在 實現上,我可以列舉...

石子歸併(區間 dp 模板)

有n堆石子排成一排,其中第i堆的石子的重量為ai,現要將石子有次序地合併成一堆。規定每次只能選相鄰的2堆合併成新的一堆,形成的新石子堆的重量以及消耗的體力都是兩堆石子的重量之和。求把全部n堆石子合併成一堆最少需要消耗多少體力。include include include include inclu...