Post

AcWing1068 - 环形石子合并

将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。

规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。

请编写一个程序,读入堆数 n 及每堆的石子数,并进行如下计算:

  • 选择一种合并石子的方案,使得做 n−1 次合并得分总和最大。
  • 选择一种合并石子的方案,使得做 n−1 次合并得分总和最小。

输入格式

第一行包含整数 n,表示共有 n 堆石子。

第二行包含 n 个整数,分别表示每堆石子的数量。

输出格式

输出共两行:

第一行为合并得分总和最小值,

第二行为合并得分总和最大值。

数据范围

1≤n≤200

输入样例:

1
2
4
4 5 9 4

输出样例:

1
2
43
54

先枚举缺口,然后转化为链式石子合并(会超时!!)

复制一条链,头尾相接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <cstring>
using namespace std;
const int N = 410, INF = 0x3f3f3f3f;

int n;
int s[N], w[N];
int f[N][N], g[N][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++) s[i] = s[i - 1] + w[i];
  
  memset(f, 0x3f, sizeof f);
  memset(g, 0xcf, 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 (len == 1) f[l][r] = g[l][r] = 0;
      else {
        for (int k = l; 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]);
  }
  
  cout << minv << endl << maxv << endl;
  
  return 0;
}
This post is licensed under CC BY 4.0 by the author.