【模板】区间DP——石子合并

1.状态定义

f[i][j] : (1) 集合: 所有将第 i 堆到第 j 堆石子合并成一堆石子的合并方式

          (2) 属性:所有合并方式中代价的最小值

2.状态计算

最后一步合并的分界线的位置来划分,如

左边1堆,右边k - 1堆

左边2堆,右边k - 2堆

左边k - 1堆,右边1堆

最后一步的合并代价为整个区间的和,因此我们可以利用前缀和解决最后一步。

曲线救国的方式,我们可以先去掉最后一步,找最小值,再加上最后一步

以k作为分界点:先求左半部分的最小代价 + 右半部分的最小代价 + 最后一步整个区间的代价

f[i][j] = min(f[i][j],f[i][k] + f[k + 1][j] + s[j] - s[i - 1]); 

我们可以枚举每个分类中找代价最小的,最终f[i][j] 取所有情况的最小值。

注意:保证计算每一个f[i][j]时,提前算好他所依赖的状态;

          计算顺序取决于区间长度。

          !区间长度为1时合并代价为 0 !

C++代码

#include <bits/stdc++.h>
using namespace std;

const int N = 310;

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

int main(){
    cin >> n;
    
    for(int i = 1; i <= n; i++) cin >> s[i];
    
    for(int i = 1; i <= n; i++) s[i] += s[i-1];   //求前缀和 - 最后一步
    
    //len = 1 ,区间长度为1时,合并代价为0
    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;
            
            for(int k = l; k < r; k++){
                f[l][r] = min(f[l][r],f[l][k] + f[k + 1][r] + s[r] - s[l-1]);
            }
        }
    }
    cout << f[1][n];   //从第1堆合并到第n堆的最小合并代价
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值