区间DP-链式石子合并

问题描述

N堆排成一排的石子,编号为1,2,…,n。每堆石子都有一堆重量,现将所有石子合并为一堆。两堆石子合并为一堆所花费代价为两堆石子的重量和,合并为的一堆的石子的重量为两堆石子重量之和。试求将所有石子合并为一堆所花费的最小代价。

输入格式

第一行输入一个数N,表示石子的堆数;第二行输入N个整数,分别表示N堆石子的重量。

输出格式

输出将N堆石子合并为一堆的最小花费代价

数据范围

1<=N<=30

输入样例

4
1 3 5 2

输出样例

22

解题思路

将N堆石子合并为一堆的过程,每一步均为两堆石子合并为一堆,石子堆数减少一;每次合并的两堆石子,可能是由最初N堆石子中的几堆组成的。

  • [l,r] 为由(l,l+1,l+2,…,r)合并为一堆的石子;每一堆石子都是由两堆石子合并得到的,则 [l,r] 石子可能是由 [l,k][k+1,r](l<=k<r)两堆石子合并得到的。
  • f[l,r] 为得到 [l,r] 这一堆石子付出的最小代价;若石子 [l,r][l,k][k+1,r] 两堆合并得到,则 得到[l,r]这堆石子付出的代价得到[l,k]这堆石子付出的代价得到[k+1,r]这堆石子将[l,k]和[k+1,r]两堆石子合并为一堆付出的代价 ,由此得到递推公式:

f[l,r]=min(f[l,k]+f[k+1,r]+s[r]-s[l-1]),(l<r)
f[l,r]=0,(l == r)

其中,s[i]为前i堆石子重量的前缀和,(l <= k < r)

  • 初值:任意i∈[1,N],f[l][l]=0,其余为正无穷
  • 目标:f[1][n]

实现代码

#include<iostream>
#include<algorithm>

using namespace std;

const int N=310;

int n;
int w[N],f[N][N];
int s[N];

int main()
{
    cin>>n;
    for(int i=1;i<=n;i++) cin>>w[i];

    //求前缀和
    for(int i=1;i<=n;i++) s[i]=s[i-1]+w[i];

    for(int len=2;len<=n;len++)
        for(int i=1;i+len-1 <= n;i++){
            int l=i,r=i+len-1;
            f[l][r]=1e8;//找不同k条件下f[l][r]的最小值,所以不希望他的初始值有影响,所以设为正无穷
            for(int k=i;k<r;k++)//枚举[l,r-1]范围内的不同区间
                f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
        }
    //输出结果即区间[1,n]的值
    cout<<f[1][n]<<endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值