72: libreoj #10147 区间dp
$des$
将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数 nnn 及每堆的石子数,并进行如下计算:
- 选择一种合并石子的方案,使得做 n−1 次合并得分总和最大。
- 选择一种合并石子的方案,使得做 n−1 次合并得分总和最小。
$sol$
经典区间dp
$code$
#include <bits/stdc++.h> using namespace std; const int N = 405; int f[N][N], w[N], sum[N]; int n; int main() { cin >> n; for(int i = 1; i <= n; i ++) { cin >> w[i]; w[i + n] = w[i]; } for(int i = 1; i <= n * 2; i ++) sum[i] = sum[i - 1] + w[i]; for(int i = 1; i <= n * 2; i ++) for(int j = 1; j <= n * 2; j ++) f[i][j] = (1 << 29); for(int i = 1; i <= n * 2; i ++) f[i][i] = 0; for(int len = 2; len <= n; len ++) { for(int l = 1; l + len - 1 <= n * 2; l ++) { int r = l + len - 1; for(int k = l + 1; k <= r; k ++) { f[l][r] = min(f[l][r], f[l][k - 1] + f[k][r]); } f[l][r] += sum[r] - sum[l - 1]; } } int Answer = (1 << 30); for(int i = 1; i <= n; i ++) Answer = min(Answer, f[i][i + n - 1]); cout << Answer << "\n"; for(int i = 1; i <= n * 2; i ++) for(int j = 1; j <= n * 2; j ++) f[i][j] = -1; for(int i = 1; i <= n * 2; i ++) f[i][i] = 0; for(int len = 2; len <= n; len ++) { for(int l = 1; l + len - 1 <= n * 2; l ++) { int r = l + len - 1; for(int k = l + 1; k <= r; k ++) { f[l][r] = max(f[l][r], f[l][k - 1] + f[k][r]); } f[l][r] += sum[r] - sum[l - 1]; } } Answer = -1; for(int i = 1; i <= n; i ++) Answer = max(Answer, f[i][i + n - 1]); cout << Answer; return 0; }