[算法系列]动态规划03-区间DP

267 阅读1分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

区间DP

区间DP,是指状态转移发生在区间上的动态规划问题,分析区间DP问题常常考虑的点为:区间分界点、端点、题中的特殊点

普通区间DP

例题:

image.png

分析:

状态表示: 用f[i] [j]表示将[i~j]闭区间合并为1堆的方案全体

状态转移: 这里就是用区间分界点作为考虑点,我们假设最后一步是将[i~k]和[k+1,j]合并为1堆,那么我们很容易写出下面的方程

f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum(i,j))f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum(i,j))

AC代码:

#include<iostream>
using namespace std;
const int N=1010;
int n;
int s[N];
int f[N][N];
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        int w;
        cin>>w;
        s[i]=s[i-1]+w;
    }
    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=n;i++){
            int j=i+len-1;
            f[i][j]=1e9;
            for(int k=i;k<j;k++){
                f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+s[j]-s[i-1]);
            }
        }
    }
    cout<<f[1][n]<<endl;
    return 0;
}

环形区间DP

例题:

image.png

image.png

处理手段: 遇到环,可以扩展两倍长度变为链

状态表示: 用f[i] [j] 表示,从边 i 到边 j运算后的所有答案总数

状态转移: 我们仍然可以考虑分界点,但是这题有负数,我们不妨求一个最小一个最大,综合考虑以得到最大的解,于是我们还可以拓展一维,f[i,j,0]表示最大值,f[i,j,1]表示最小值,状态转移方程如下:

f[i][j][0]=max(f[i][j][0],cal(f[i][k][a],f[k+1][j][b],op[k+1]))f[i][j][0]=max(f[i][j][0],cal(f[i][k][a],f[k+1][j][b],op[k+1]))

方程中a,b是两个待枚举值,cal函数是计算相应运算符号对应的值,最小值的情况与之类似:

f[i][j][1]=min(f[i][j][1],,cal(f[i][k][a],f[k+1][j][b],op[k+1]))f[i][j][1]=min(f[i][j][1],,cal(f[i][k][a],f[k+1][j][b],op[k+1]))

AC代码:

#include<iostream>
using namespace std;
const int N=120;
const int INF=1e9;
int n;
char op[N];
int q[N];
int f[N][N][2];
​
int cal(int a,int b,char op){
    if(op=='t'){
        return a+b;
    }
    return a*b;
}
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>op[i]>>q[i];
        op[i+n]=op[i];q[i+n]=q[i];
    }
    for(int i=1;i<=2*n;i++){
        for(int j=1;j<=2*n;j++){
            if(i==j)f[i][j][0]=f[i][j][1]=q[i];
            else{f[i][j][0]=-INF;f[i][j][1]=INF;}
        }
    }
    for(int len=2;len<=n;len++){
        for(int i=1;i+len-1<=2*n;i++){
            int j=i+len-1;
            for(int k=i;k<j;k++){
                for(int a=0;a<=1;a++){
                    for(int b=0;b<=1;b++){
                        f[i][j][0]=max(f[i][j][0],cal(f[i][k][a],f[k+1][j][b],op[k+1]));
                        f[i][j][1]=min(f[i][j][1],cal(f[i][k][a],f[k+1][j][b],op[k+1]));
                        
                    }
                }
            }
        }
    }
    int res[20002];
    int idx=0,maxn=-INF;
    for(int i=1;i<=n;i++){
        int t=f[i][i+n-1][0];
        if(t>maxn){
            idx=0;maxn=t;res[idx++]=i;
        }
        else if(t==maxn){
           res[idx++]=i; 
        }
    }
    cout<<maxn<<endl;
    for(int i=0;i<idx;i++){
        cout<<res[i]<<" ";
    }
    return 0;
}