区间dp(模板)

52 阅读2分钟

区间合并的思路:先在小区间内的到最优解,再利用小区间的最优解合并,进而的到大区间的最优解,最终的到整个区间的最优解

石子合并(弱化版)

题目描述

设有 N(N300)N(N \le 300) 堆石子排成一排,其编号为 1,2,3,,N1,2,3,\cdots,N。每堆石子有一定的质量 mi (mi1000)m_i\ (m_i \le 1000)。现在要将这 NN 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

输入格式

第一行,一个整数 NN

第二行,NN 个整数 mim_i

输出格式

输出文件仅一个整数,也就是最小代价。

样例 #1

样例输入 #1

4
2 5 3 1

样例输出 #1

22

分析题目: 每次只能合并相邻的两堆石子,合并的代价是这两堆石子的质量之和, 例如合并第i堆石子和第j堆石子,则合并代价是a[i]+a[j];

思路:

每次合并两堆石子,选择其中两个相邻之和最小的进行合并 状态表示: f[i][j] 表示 合并第i堆石子到第j堆石子合并成一堆石子所用代价的最小值 分割点分析 : 注意我们以最后两堆石子为分割点,设最后剩下的两堆石子为:f[i][k],f[k+1][j],这两堆合并还需要加上这两堆石子的代价和

所以得出f[i][j]的状态转移方程为:f[i][k] + f[k+1][j] + s[j] - s[i-1];

#include<iostream>
#include<cstring>
const int N = 310;
int a[N], s[N], f[N][N];//a[i]存储每个石子的重量,s[i]表示 前i堆石子重量和;
int main() {
    std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
    int n;
    std::cin >> n;
    memset(f, 0x3f, sizeof f); //求最小值,设置f数组无穷大
    for(int i = 1; i <= n; i++) std::cin >> a[i], s[i] = s[i - 1] + a[i], f[i][i] = 0; //f[i][i] = 0,表示一堆石子不需要进行合并


    for(int len = 2; len <= n; len++) { //区间的长度
        for(int i = 1; i + len - 1 <= n; i++) { //合并的起点 i +len-1 退出合并的右侧端点
            int j = len + i - 1;                //合并的终点
            for(int k = i; k < j; k++) {        //枚举分割点
                f[i][j] = std::min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
            }
        }
    }
    std::cout << f[1][n] << '\n';
    return 0;
}