282. 石子合并
状态转移方程:
\(f(i, j) = min\{s[j] - s[l - 1] + f[i][k] + f[k + 1][j]\} (k = i, i + 1, ..., r - 1)\)
第一种(循环区间长度和左端点)
#include<iostream>
using namespace std;
const int N = 310, INF = 0x3f3f3f3f;
int f[N][N];
int a[N], s[N];
int n;
int main(){
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) s[i] = a[i] + s[i - 1];
/*
1. 合并一堆石子的代价为0 <=> f[i][i] = 0
2. len必须从2开始否则得到的矩阵会是[INF]nxn
3. 使用枚举两个区间端点的时候需要考虑保证之前的状态被计算过
*/
for(int len = 2; len <= n; len ++)
for(int l = 1; l + len - 1 <= n; l ++){
int r = l + len - 1;
f[l][r] = INF;
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];
return 0;
}
第二种(循环两个区间端点)
#include<iostream>
using namespace std;
const int N = 310, INF = 0x3f3f3f3f;
int f[N][N];
int a[N], s[N];
int n;
int main(){
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) s[i] = a[i] + s[i - 1];
/*
k + 1必定大于i,而此时f[k + 1][j]还没有被计算,
这就是为什么循环i要倒着来的原因了。
*/
for(int i = n; i >= 1; i --)
for(int j = i + 1; j <= n; j ++){
f[i][j] = INF;
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]);
}
cout << f[1][n];
return 0;
}