1. 程式人生 > >區間DP模板題

區間DP模板題

通用模板

//mst(dp,0) 初始化DP陣列
for(int i=1;i<=n;i++)
{
    dp[i][i]=初始值
}
for(int len=2;len<=n;len++)  //區間長度
for(int i=1;i<=n;i++)        //列舉起點
{
    int j=i+len-1;           //區間終點
    if(j>n) break;           //越界結束
    for(int k=i;k<j;k++)     //列舉分割點,構造狀態轉移方程
    {
        dp[i][j]=max(dp[
i][j],dp[i][k]+dp[k+1][j]+w[i][j]); } }

石子合併(一)
http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=737

石子合併(一)
時間限制:1000 ms | 記憶體限制:65535 KB

難度:3

描述

有N堆石子排成一排,每堆石子有一定的數量。現要將N堆石子併成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過N-1次合併後成為一堆。求出總的代價最小值。

輸入

有多組測試資料,輸入到檔案結束。
每組測試資料第一行有一個整數n,表示有n堆石子。
接下來的一行有n(0< n <200)個數,分別表示這n堆石子的數目,用空格隔開

輸出

輸出總代價的最小值,佔單獨的一行

樣例輸入

3

1 2 3

7

13 7 8 16 21 4 18

樣例輸出

9

239

#include<bits/stdc++.h>
using namespace std;
const int maxn=500;
int dp[maxn][maxn],sum[maxn],x;
int main(){
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0x3f,sizeof(dp));
        memset
(sum,0,sizeof(sum)); for(int i=1;i<=n;i++) { scanf("%d",&x); sum[i]=sum[i-1]+x; dp[i][i]=0; } for(int len=2;len<=n;len++) { for(int i=1;i<=n;i++) { int j=i+len-1; if(j>n) break; for(int k=i;k<j;k++) { dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } } } printf("%d\n",dp[1][n]); } return 0; }

平行四邊形優化DP
一般來說,代價值如w[i][j]可以表示為sum[j]-sum[i-1]時就可以用,也就是說代價值可以用字首和表示出來。

用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<bits/stdc++.h>
using namespace std;
const int maxn=500;
int dp[maxn][maxn],sum[maxn],x,s[maxn][maxn];
int main(){
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        memset(dp,0x3f,sizeof(dp));
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&x);
            sum[i]=sum[i-1]+x;
            dp[i][i]=0;
            s[i][i]=i;
        }
        for(int len=2;len<=n;len++)
        {
            for(int i=1;i<=n;i++)
            {
                int j=i+len-1;
                if(j>n) break;
                for(int k=s[i][j-1];k<=s[i+1][j];k++)
                {
                    if(dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]<dp[i][j]){
                        dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                        s[i][j]=k;
                    }

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