石子合并类似的问题,要求找出括号的添加方法,最小中间和之和,各个中间和.
设dp[i][j]为i到j之间最小中间和之和
则dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + sum[i][j] | i<= k < j) sum[i][j]为i到j的和
base case:dp[i][i] = 0
#include <cstdio>
#include <climits>
#include <memory.h>
using namespace std;
const int MAX = 23;
int dp[MAX][MAX];
int pos[MAX][MAX];
int val[MAX], sum[MAX][MAX];
void print_path(int i, int j){
if(i == j){
printf("%d", val[i]);
return;
}
printf("(");
print_path(i, pos[i][j]);
printf("+");
print_path(pos[i][j] + 1, j);
printf(")");
}
int print_intermedia(int x, int y){
if(x == y)return val[x];
int v = print_intermedia(x, pos[x][y]) + print_intermedia(pos[x][y] + 1, y);
printf("%d ", v);
return v;
}
int main(int argc, char const *argv[]){
int N;
scanf("%d", &N);
for(int i = 1; i <= N; ++i){
scanf("%d", &val[i]);
}
memset(sum, -1, sizeof(sum));
for(int i = 1; i <= N; ++i){
dp[i][i] = 0;
pos[i][i] = i;
sum[i][i]= val[i];
for(int j = i + 1; j <= N; ++j){
sum[i][j] = sum[i][j - 1] + val[j];
}
}
for(int j = 1; j < N; ++j){
for(int i = 1; i + j <= N; ++i){
int opt_v = INT_MAX, opt_p = 0;
for(int k = i + j - 1; k >= i; --k){
if(opt_v > dp[i][k] + dp[k + 1][i + j] + sum[i][i + j]){
opt_v = dp[i][k] + dp[k + 1][i + j] + sum[i][i + j];
opt_p = k;
}
}
pos[i][i + j] = opt_p;
dp[i][i + j] = opt_v;
}
}
print_path(1, N);
printf("\n");
printf("%d\n", dp[1][N]);
print_intermedia(1, N);
printf("\n");
return 0;
}