题目描述
给定n个数,只能合并相邻的两个数,求合并到最后的时候花费的价值最小
合并花费的费用是两个数相加
样例
输入样例:
4
1 3 5 2
输出样例:
22
参考文献
作者:捡到一束光
链接:(https://www.acwing.com/solution/content/13945/)
来源:AcWing
算法1
(区间dp)
解题关键:
每次合并都是左边一段连续区间和右边一段连续区间进行合并
那么枚举整段区间的这个切割点k
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + 这一段的区间和(s[j] - s[i - 1]))
区间dp问题
三重循环
第一重枚举区间长度 从1~n 一般长度为1的时候是初始化区间
第二重循环枚举左端点i 右端点j根据区间长度自动获取,不需要再枚举
第三重循环枚举区间的切割点k
C++ 代码
#include<bits/stdc++.h>
using namespace std;
const int N = 310;
int f[N][N];
int s[N];
int n;
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> s[i];
s[i] += s[i - 1];
}
memset(f,0x3f,sizeof f); // 因为找的是最小值,所以要将f数组初始化为最大值
for(int len = 1; len <= n; len ++)
{
for(int i = 1; len + i - 1 <= n; i ++)
{
int j = len + i - 1;
if(len == 1 )
{
f[i][j] = 0;
continue;
}
for(int k = i; k < j; k ++) // 这里要注意切割点可以等于左端点 但不能等于右端点
{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
// 如果k等于左端点就是左边一个右边len-1个 如果k等于右端点会爆
}
}
}
cout << f[1][n] << endl;
return 0;
}
算法2
(记忆化搜索)
C++ 代码
// 记忆化搜索
#include<bits/stdc++.h>
using namespace std;
const int N = 310;
int n;
int s[N];
int t[N];
int f[N][N];
int dfs(int a, int b)
{
if(a == b) return 0;
int & v = f[a][b];
if(v != 0x3f3f3f3f) return v;
for(int k = a; k < b; k ++)
{
v = min(v, dfs(a,k) + dfs(k + 1,b) + s[b] - s[a - 1]);
}
return v;
}
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++)
{
cin >> t[i];
s[i] += t[i] + s[i - 1];
}
memset(f,0x3f,sizeof f);
cout << dfs(1, n) << endl;
return 0;
}