问题描述:在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆,规定每次只能选相邻的2堆石子合并成一堆,并将新的堆石子数记为该次合并的得分。试设计一个算法,计算出将n堆石子合并成一堆的最小得分和最大得分。
算法设计:对于给定n堆石子,计算合并成一堆的最小得分和最大得分。
数据输入:由文件 input.txt
提供输入数据。文件的第1行是正整数$n(1\leq n \leq 100)$,表示有n堆石子。第2行有n个数,分别表示每堆石子的个数。
结果输出:将计算结果输出到文件 output.txt
。文件第1行的数是最小得分,第1行中的数是最大得分。
 
#include<iostream> #include<vector> using namespace std; #define MAXN 100010 /* 联系例题--矩阵链乘,一毛一样 4 4 4 5 9 圆形操场,代表第一堆石子与最后一堆石子相邻。直接将数组定义为[4,4,5,9,4,4,5,9],就可以用[...9,4...]代表头尾相连;因此可以设数组长度为2n,数组下标为0~2n-1,但遍历数组时,宽度最大还是n 设dp[i][j]表示第i个石堆到第j个石堆合并的最小分;weight[i]表示前i个石堆的重量,weight[0]=0; 假设dp[i][j]是由i~k和k+1~j合并得到的,则有dp[i][j]=min(dp[i][k]+dp[k+1][j]+(weight[j]-weight[i-1])); */ int n;//石堆数 int weight[MAXN];//前i个石堆累积重量 vector<vector<int>> dp; void init() { cin >> n; weight[0] = 0; for (int i = 1; i <= n; i++) { cin >> weight[i]; } for (int i = n + 1; i <= 2 * n; i++) { weight[i] = weight[i - n]; } for (int i = 1; i <= 2*n; i++) { weight[i] += weight[i - 1];//0,4,8,13,22.26,30,35,44 } int m = 2 * n; dp.resize(m, vector(m, 1e9)); for (int i = 0; i < m; i++) { dp[i][i] = 0;//只有一堆合并不了,得分为0 } } void solve_min_score() { //填数组 //因为计算dp[i][j]要先计算dp[i][k]和dp[k+1][j]。计算大区间要先计算小区间,因此最外层循环枚举len长度,确保在后面计算大区间时已经有了小区间的dp值。 for (int len = 2; len <= 2 * n; len++) {//枚举区间长度,长度为1表示一堆石堆,因此不用考虑 for (int i = 0; i <= 2 * n - len; i++) {//枚举左端点 int j = i + len - 1;//右端点 for (int k = i; k < j; k++) {//枚举分割点 dp[i][j] = min(dp[i][j] ,dp[i][k] + dp[k + 1][j] + weight[j+1] - weight[i]); } } } for (int i = 0; i < 2 * n; i++) { for (int j = 0; j < 2 * n; j++) { cout<<dp[i][j]<<" "; } cout<<endl; } // 计算最小得分 int min_score = 1e9; for (int i = 0; i < n; i++) { // 枚举所有长度为 n 的环形区间 min_score = min(min_score, dp[i][i + n - 1]); } cout << min_score << endl; } int main() { init(); solve_min_score(); return 0; }
最新评论