区间dp

区间dp模板题  

P1880 [NOI1995]石子合并

先上状态转移方程

mindp[i][j]=min(mindp[i][k]+mindp[k+1][j]+d(i,j),mindp[i][j]); 
maxdp[i][j]=max(maxdp[i][k]+maxdp[k+1][j]+d(i,j),maxdp[i][j]);  

  

其中d(i,j)意义为从第i堆加到第j堆的个数和

mindp[i][j]代表第i堆到第j堆的合并最小分数

maxdp[i][j]代表第i堆到第j堆的合并最大分数

到了这里 其实这道题就应该迎刃而解了 但是我还是卡了很久

原因是什么呢

这道题 是TM环状

所以我们需要将环拆开 变成一个2n长度的链

讲到这里 题应该已经结束了 但是还有一个关键点

就是 我们应该怎么遍历k呢

先放代码

#define rep(i,a,b) for(int i = a;i <= b;i++)
    rep(l,2,n){
        rep(i,1,2*n-l+1){
            int j = i+l-1;
            mindp[i][j] = 1000000000;
            maxdp[i][j] = 0;
            rep(k,i,j-1){
                mindp[i][j] = min(mindp[i][k]+mindp[k+1][j]+d(i,j),mindp[i][j]);
                maxdp[i][j] = max(maxdp[i][k]+maxdp[k+1][j]+d(i,j),maxdp[i][j]);
             }    
        }    
    }

在考虑如何递推时,通常考虑如下几个方面:

是否能覆盖全部状态?

求解后面状态时是否保证前面状态已经确定?

是否修改了已经确定的状态?

也就是说,在考虑递推顺序时,务必参考动态规划的适应对象多具有的性质,具体参考《算法导论》相关或百度百科或wiki。

既然之前说过我们需要枚举k来划分i和j,那么如果通过枚举i和j进行状态转移,很显然某些k值时并不能保证已经确定过所需状态。

如,i=1 to 10,j=1 to 10,k=1 to 9.当i=1,j=5,k=3时,显然状态f[k+1][j]没有结果。

那么,我们是不是应该考虑枚举k?

但这样i和j就难以确定了。

我们不难得到一个两全的方法:枚举j-i,并在j-i中枚举k。这样,就能保证地推的正确。

ac代码

#include "cstdio"
#include "iostream"
#include "algorithm"
#include "string.h"
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int e = 210;
int n;
int k;
int maxdp[e][e],mindp[e][e];
int psum[e];
int T[e];
inline int d(int i,int j){
    return psum[j]-psum[i-1];
}
int main(){
    cin >> n;
    rep(i,1,n){
        cin >> T[i];
        T[i+n]=T[i];
    }
    rep(i,1,2*n){
        psum[i]=psum[i-1]+T[i];
    }
    rep(l,2,n){
        rep(i,1,2*n-l+1){
            int j = i+l-1;
            mindp[i][j] = 1000000000;
            maxdp[i][j] = 0;
            rep(k,i,j-1){
                mindp[i][j] = min(mindp[i][k]+mindp[k+1][j]+d(i,j),mindp[i][j]);
                maxdp[i][j] = max(maxdp[i][k]+maxdp[k+1][j]+d(i,j),maxdp[i][j]);
             }    
        }    
    }
    int minn = 1000000000,maxx = 0;
    rep(i,1,n){
        minn = min(minn,mindp[i][i+n-1]);
        maxx = max(maxx,maxdp[i][i+n-1]);
    }
    printf("%d
%d",minn,maxx);
    return 0;
}
人十我百 人百我万
原文地址:https://www.cnblogs.com/bestcoder-Yurnero/p/10692583.html