【题解】石子合并

区间dp模板题

题目链接

题目要求一个环中的最小价值和最大价值(如题)。

首先,我们段环成链,即开两倍的空间,i与i+n对应。

用前缀和预处理出任意区间中的和。

接下来,我们枚举段环的位置。

设dp[i][j]是i到j合并的最小价值。

显然我们可以枚举k作为段环的点,方程即为:

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

就是将i到k的最小价值和k+1到j的最小价值与i到j的石子数量总和加起来,取最小。

设f[i][j]是i到j合并的最大价值,同理。

我们枚举i到j,并在i到j之间枚举k作为断点。

每次dp即可。

最后需要分别求一遍最大最小值。

代码:

#include<cstdio>
#include<iostream>
#define MAXN 2147483647
#include<cstring>
using namespace std;
int n,sum[50000];
int a[50000],minn,maxn;
int f[500][500];//max
int dp[500][500];//min
inline int dis(int i,int j){return sum[j]-sum[i-1];}
inline void read(int &x){
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        s=(s<<1)+(s<<3)+(ch^48);
        ch=getchar();
    }
    x=s*w;
}
int main(){
    read(n);
    for(int i=1;i<=(n);++i){
        scanf("%d",&a[i]);
        a[i+n]=a[i];
    }
    for(int i=1;i<=(n<<1);i++)
        sum[i]=sum[i-1]+a[i];
    for(int p=1;p<n;p++){
        for(int i=1,j=i+p;(j<(n<<1))&&(i<(n<<1));i++,j=i+p){
            dp[i][j]=MAXN;
            for(int k=i;k<j;++k){
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+dis(i,j));
                f[i][j] =max (f[i][j],f[i][k] +f [k+1][j]+dis(i,j));
            }
        }
    }
    minn=MAXN;
    maxn=-MAXN;
    for(int i=1;i<=n;i++){
        minn=min(dp[i][i+n-1],minn);
        maxn=max(f[i][i+n-1],maxn);
    }
    printf("%d
%d",minn,maxn);
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/h-lka/p/11146452.html