[P1880] [NOI1995]石子合併(環形dp)
阿新 • • 發佈:2020-08-07
題目描述
在一個圓形操場的四周擺放 \(N\) 堆石子,現要將石子有次序地合併成一堆.規定每次只能選相鄰的2堆合併成新的一堆,並將新的一堆的石子數,記為該次合併的得分。
試設計出一個演算法,計算出將\(N\)堆石子合併成 \(1\) 堆的最小得分和最大得分。
輸入格式
資料的第 \(1\) 行是正整數 \(N\),表示有 \(N\) 堆石子。
第 \(2\) 行有 \(N\) 個整數,第 \(i\) 個整數 \(a_i\) 表示第 \(i\) 堆石子的個數。
輸出格式
輸出共 \(2\) 行,第 \(1\) 行為最小得分,第 \(2\) 行為最大得分。
輸入輸出樣例
輸入 #1
4 4 5 9 4
輸出 #1
43
54
說明/提示
\(1≤N≤100,0≤ai≤200。\)
【思路】
拆環,把鏈的長度延長到\(2n\), 區間dp做法,最後列舉左端點取其中最大/最小的結果。
#include <algorithm> #include <cmath> #include <cstdio> #include <cstring> #include <list> #include <map> #include <iostream> #include <iomanip> #include <queue> #include <set> #include <stack> #include <string> #include <unordered_map> #include <vector> #define LL long long #define inf 0x3f3f3f3f #define INF 0x3f3f3f3f3f3f #define PI 3.1415926535898 #define F first #define S second #define endl '\n' #define lson rt << 1 #define rson rt << 1 | 1 #define f(x, y, z) for (LL x = (y), __ = (z); x < __; ++x) #define _rep(i, a, b) for (LL i = (a); i <= (b); ++i) using namespace std; const int maxn = 207; const int maxm = 2e4 + 7; const int mod = 19650827; int n; int a[maxn], dpmx[maxn][maxn], dpmn[maxn][maxn], sum[maxn]; int main() { ios::sync_with_stdio(false); cin.tie(0); cin >> n; memset(dpmx, 0, sizeof(dpmx)); memset(dpmn, inf, sizeof(dpmn)); _rep(i, 1, n) { cin >> a[i]; a[i + n] = a[i]; } _rep(i, 1, 2 * n) { sum[i] = sum[i - 1] + a[i]; dpmn[i][i] = 0; } _rep(len, 1, n) { _rep(l, 1, n * 2 - len) { int r = l + len; _rep(k, l, r - 1) { dpmx[l][r] = max(dpmx[l][r], dpmx[l][k] + dpmx[k + 1][r] + sum[r] - sum[l - 1]); dpmn[l][r] = min(dpmn[l][r], dpmn[l][k] + dpmn[k + 1][r] + sum[r] - sum[l - 1]); } } } int mx = 0, mn = inf; _rep(i, 1, n) { mx = max(mx, dpmx[i][i + n - 1]); mn = min(mn, dpmn[i][i + n - 1]); } cout << mn << endl << mx << endl; }