区间dp

#include <bits/stdc++.h>
using namespace std;
#define maxn 2000
const int maxm=5000;
int f[maxn][maxn],a[maxm],sum[maxm],n;
inline int read(){
    int num=0,f=1; char c=getchar();
    while(!isdigit(c)){if(c=='-') f=-1; c=getchar();}
    while(isdigit(c)){num=((num<<1)+(num<<3))+(c^48); c=getchar();}
    return num*f;
}
int in(){
    memset(f,10,sizeof(f));
    n=read();
    for(int i=1;i<=n;i++){
        a[i]=read(); sum[i]+=sum[i-1]+a[i]; f[i][i]=0;
    }
}
int work(){
    for(int l=1;l<=n;l++){
        for(int i=1;i+l<=n;i++){
            int j=i+l;
            for(int k=i;k<i+l;k++){
                f[i][j]=min(f[i][k]+f[k+1][j]+sum[j]-sum[i-1],f[i][j]);
            }
        }
    }
    cout<<f[1][n];
}
int main(){
    in();
    work();
}
区间dp(大佬勿喷)
 
有n堆石子排成一排,每堆石子都有一个大小。每次你可以合并相邻两堆石子,消耗的体力为两堆石子大小总和,合并后的石子位置为原来两堆石子的位置,合并后的石子大小为两堆石子大小总和。
求最小的总体力消耗。
n<=100
进阶数据范围:n<=1000

我们注意到,每堆石子都一定是由一段连续的区间合并而来。
且一段区间合并后的总大小是确定的。
于是我们设dp[l][r]表示区间[l,r]之间的石子合并成一堆所需要的最小体力。
考虑最后一次合并,一定只剩2堆石子。
而这2堆石子一定也是由连续的区间合并而来的。
所以我们可以枚举左边区间的右端点(右边区间的左端点)。
for (int k=l;k<r;++k) dp[l][r]=max(dp[l][r],dp[l][k]+dp[k+1][r]+sum(l,r));
其中sum(l,r)表示区间[l,r]的总和。可以通过预处理来支持快速查询。
状态:O(n^2);转移:O(n)。总复杂度:O(n^3)。

 

posted @ 2017-09-28 21:14  TimDucan  阅读(121)  评论(0编辑  收藏  举报