区间dp经典例题之合并石子

#区间dp之合并石子(经典例题)
石子合并
题目大概意思是 n 堆石子摆成一个圆形,每次只能用相邻的两堆石子进行合并,并且得分为这两堆石子的重量总和,问你你可取得的最大分数和最小分数是多少?

样例输入
4
4 5 9 4

样例输出
43
54

我们可以把环状的石子线性化,即每次取两堆石子,最后合成一堆,我们可以把它想象成从某个开口切开这个环,并形成一条直线,这样我们就把这个问题简化成了直线上的取石子问题。

dp可以看做优化之后的枚举,在这个问题里,我们每次枚举区间的长度、起点,分割点,计算以这个起点开始,这个长度的区间内,可以获得的最优解。

假设分割点为k,起点i,终点j,那么状态转移方程就可以表示为dp[ i ][ j ]=max(dp[ i ][ j ],dp[i][k]+dp[k+1][j]+sum[j]-sum[i+1])。

代码

#include
using namespace std;
const int N=1e3+5;
const int inf=0x3f3f3f3f;
int a[N],sum[N];
int dpmax[N][N],dpmin[N][N];
int main()
{
    int n;
    cin>>n;
    memset(dpmax,-1,sizeof dpmax);
    memset(dpmin,inf,sizeof dpmin);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        sum[i]=sum[i-1]+a[i];
        dpmax[i][i]=dpmin[i][i]=0;//初始化
    }
    for(int i=n+1;i<=n*2;i++)//化成线性
    {
        sum[i]=sum[i-1]+a[i-n];
        dpmax[i][i]=dpmin[i][i]=0;
    }
    for(int len=1;len<=n;len++)//枚举长度
    {
        for(int j=1;j<=n*2-len;j++)
        {
            int ends=j+len-1;
            for(int i=j;i<ends;i++)
            {
                dpmax[j][ends]=max(dpmax[j][ends],dpmax[j][i]+dpmax[i+1][ends]+sum[ends]-sum[j-1]);
                dpmin[j][ends]=min(dpmin[j][ends],dpmin[j][i]+dpmin[i+1][ends]+sum[ends]-sum[j-1]);
            }
        }
    }
    int maxans=-1,minans=inf;
    for(int i=1;i<=n;i++)
    {
            maxans=max(maxans,dpmax[i][i+n-1]);
            minans=min(minans,dpmin[i][i+n-1]);
    }
    cout<<minans<<endl<<maxans<<endl;
}

你可能感兴趣的:(区间dp)