区间合并的思路:先在小区间内的到最优解,再利用小区间的最优解合并,进而的到大区间的最优解,最终的到整个区间的最优解
石子合并(弱化版)
题目描述
设有 堆石子排成一排,其编号为 。每堆石子有一定的质量 。现在要将这 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。
输入格式
第一行,一个整数 。
第二行, 个整数 。
输出格式
输出文件仅一个整数,也就是最小代价。
样例 #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;
}