问题描述:在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
输入文件:
input.txt
4
4 4 5 9
输出文件:
output.txt
43
54
算法思路:仿照动态规划求解矩阵连乘问题的方式,与矩阵连乘问题不同的是,它的结构是一个环。
#include<iostream>
const int N = 64;
int sum[N];
int arr[N];
int dp_min[N][N];
int dp_max[N][N];
//求最小值
int min(int lhs, int rhs){
return lhs < rhs ? lhs : rhs;
}
//求最大值
int max(int lhs, int rhs){
return lhs > rhs ? lhs : rhs;
}
void computer(int arr[], int arr_length, int& min_num, int& max_num){
for (int i = 0; i < arr_length; i++){
dp_min[i][i] = 0;
}
for (int r = 1; r < arr_length; r++){
for (int i = 0; i < arr_length; i++){
int j = (i + r) % arr_length;
int tm;
if (j > i){
tm = sum[j] - (i > 0 ? sum[i - 1] : 0);
}
else{
tm = sum[arr_length - 1] - sum[i - 1] + sum[j];
}
dp_min[i][j] = dp_min[i][(i + 1) % arr_length] + dp_min[(i + 2) % arr_length][j] + tm;
dp_max[i][j] = dp_max[i][(i + 1) % arr_length] + dp_max[(i + 2) % arr_length][j] + tm;
for (int k = i, index = i ; index < i + r; index++){
dp_min[i][j] = min(dp_min[i][j], dp_min[i][k] + dp_min[(k + 1) % arr_length][j] + tm);
dp_max[i][j] = max(dp_max[i][j], dp_max[i][k] + dp_max[(k + 1) % arr_length][j] + tm);
k = (k + 1) % arr_length;
}
}
}
min_num = dp_min[0][arr_length - 1];
max_num = dp_max[0][arr_length - 1];
for (int i = 0; i < arr_length; i++){
min_num = min(min_num, dp_min[i][(i + arr_length - 1) % arr_length]);
max_num = max(max_num, dp_max[i][(i + arr_length - 1) % arr_length]);
}
}
int main(void){
int n = -1;
int min_num, max_num;
std::cin >> n;
for (int count = 0; count < n; count++){
std::cin >> arr[count];
sum[0] = arr[0];
for (int i = 1; i < n; i++){
sum[i] = sum[i - 1] + arr[i];
}
}
computer(arr, n, min_num, max_num);
std::cout << min_num << "\n" << max_num << std::endl;
return 0;
}