1. 程式人生 > 其它 >1569:【 例 1】石子合併

1569:【 例 1】石子合併

1569:【 例 1】石子合併


時間限制: 1000 ms 記憶體限制: 524288 KB
提交數: 1928 通過數: 1116

【題目描述】

將 $n$ 堆石子繞圓形操場排放,現要將石子有序地合併成一堆。規定每次只能選相鄰的兩堆合併成新的一堆,並將新的一堆的石子數記做該次合併的得分。

請編寫一個程式,讀入堆數 $n$ 及每堆的石子數,並進行如下計算:

1、選擇一種合併石子的方案,使得做 $n-1$ 次合併得分總和最大。

2、選擇一種合併石子的方案,使得做 $n-1$ 次合併得分總和最小。

【輸入】

輸入第一行一個整數 $n$,表示有 $n$ 堆石子。

第二行 $n$ 個整數,表示每堆石子的數量。

【輸出】

輸出共兩行:

第一行為合併得分總和最小值,

第二行為合併得分總和最大值。

【輸入樣例】

4

4 5 9 4

【輸出樣例】

43

54

【提示】

資料範圍與提示:

對於 100% 的資料,有 $1≤n≤200$。

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 410, INF = 0x3f3f3f3f;
int f[N][N] ,g[N][N];
int s[N];
int n;
int w[N];
int
main() { scanf("%d",&n); for(int i = 1; i <= n; i++) { scanf("%d",&w[i]); w[i + n] = w[i]; } for(int i = 1 ;i <= n * 2; i++) { s[i] = s[i - 1] + w[i]; } memset(f, 0x3f, sizeof f); memset(g, -0x3f, sizeof g); for(int len = 1
; len <= n; len++) { for(int l = 1 ; l + len - 1 <= n * 2; l++) { int r = l + len - 1; if(l == r) f[l][r] = g[l][r] = 0; else { for(int k = 1; k < r; k++) { f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + s[r] - s[l - 1]); g[l][r] = max(g[l][r], g[l][k] + g[k + 1][r] + s[r] - s[l - 1]); } } } } int minv = INF, maxv = -INF; for(int i = 1; i <= n; i++) { minv = min(minv, f[i][i + n - 1]); maxv = max(maxv, g[i][i + n - 1]); } printf("%d\n%d\n",minv, maxv); return 0; }