1. 程式人生 > >洛谷P1880 [NOI1995]

洛谷P1880 [NOI1995]

題解連結:

題目連結:

題目:

題目描述

在一個圓形操場的四周擺放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,

min[l][r]表示l~r這個區間內的所有石子合併成一堆獲得的最小權值,sum為字首和陣列,則有:

min[l][r]=min(min[l][r],min[l][mid]+min[mid+1][r]+sum[r]sum[l])

  求max時也同理,值得注意的是,因為是一個圈,所以要化環為鏈把陣列的長度增長一下。

實現:

#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; }