分析
这个问题是区间DP的模板题。区间DP的特点是把序列划分成左右两个子序列,子序列对应于子问题。递归求解左、右两个子问题,得到最优解后,再想办法合并为当前断开方案的最优解。
划分的断开点不同,对应于不同的左、右子问题,递归解决子问题后,就得到不同断开方案的最优解。从而进一步算出整个问题的最优解。
定义状态dp[i][j]表示区间[i,j]合并为一个数的操作代价之和。
考虑状态转移方程。枚举断开点k,也就是最后一次合并的分界线,把序列分成左右两个子序列,先把左右两个子序列分别递归合并成一个数,最小操作代价为dp[i][k]和dp[k+1][j]。最后一次合并的操作代价很简单,就是左右两个数直接相加,本质上是区间所有数之和。
状态转移方程可写为:dp[i][j] = min(dp[i][k], dp[k+1][j]) + sum(i,j) | i <= k <= j - 1
边界状态为dp[i][i] = 0
在实现时,要把区间长度作为阶段变量放在最外层枚举。
for(int len = 2; len <= n; len++)
for(int i = 1; i + len - 1 <= n; i++){
int j = i + len - 1;
dp[i][j] = INT_MAX;
for(int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]);
dp[i][j] += sum[j] - sum[i-1];
}
完整的代码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 5005
#define INF 0x3F3F3F3F
int a[MAXN], sum[MAXN], dp[MAXN][MAXN];
int n;
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++){
scanf("%d", &a[i]);
sum[i] = sum[i-1] + a[i];
}
for(int len = 2; len <= n; len++)
for(int i = 1; i<= n-len+1; i++){
int j = i+len-1;
dp[i][j] = INF;
for(int k = i; k < j; k++)
dp[i][j] = min(dp[i][j], dp[i][k]+dp[k+1][j]);
dp[i][j] += sum[j] - sum[i-1];
}
printf("%d\n", dp[1][n]);
}