洛谷P1880 [NOI1995]
阿新 • • 發佈:2019-02-03
題解連結:
題目連結:
題目:
題目描述
在一個圓形操場的四周擺放N堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。
試設計出1個演算法,計算出將N堆石子合併成1堆的最小得分和最大得分.
輸入格式:
資料的第1行試正整數N,1≤N≤100,表示有N堆石子.第2行有N個數,分別表示每堆石子的個數.
輸出格式:
輸出共2行,第1行為最小得分,第2行為最大得分.
輸入樣例#1:
4
4 5 9 4
輸出樣例#1:
43
54
思路:
看到n
的範圍很小,考慮區間DP, 表示~這個區間內的所有石子合併成一堆獲得的最小權值,為字首和陣列,則有:
求時也同理,值得注意的是,因為是一個圈,所以要化環為鏈把陣列的長度增長一下。
實現:
#include <bits/stdc++.h>
const int maxn = 207 ;
int n, num[maxn], min[maxn][maxn], max[maxn][maxn], sum[maxn], ans_min = 0x3f3f3f3f, ans_max;
int main() {
// freopen("in.txt", "r", stdin);
scanf("%d", &n);
memset(min, 0x3f, sizeof(min));
for (int i = 1; i <= n; i++) scanf("%d", num + i), num[n + i] = num[i];
for (int i = 1 ; i <= n << 1; i++) min[i][i] = 0, sum[i] = sum[i - 1] + num[i];
for (int len = 2; len <= n; len++)
for (int l = 1, r; (r = l + len - 1) <= n << 1; l++)
for (int mid = l; mid < r; mid++) {
min[l][r] = std::min(min[l][r], min[l][mid] + min[mid + 1][r] + sum[r] - sum[l - 1]);
max[l][r] = std::max(max[l][r], max[l][mid] + max[mid + 1][r] + sum[r] - sum[l - 1]);
}
for (int i = 1; i <= n; i++) {
ans_min = std::min(ans_min, min[i][i + n - 1]);
ans_max = std::max(ans_max, max[i][i + n - 1]);
}
printf("%d\n%d\n", ans_min, ans_max);
return 0;
}