区间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<bits/stdc++.h>
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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值