目录

P1880 [NOI1995] 石子合并

目录

原理

因为操场是个环,将环展开到n+n的数组里,同时也把a复制一份

s求前缀和,用于在calc函数中求出从i到j堆石子的个数和

dpmax和dpmin字面意思,存在合并i到j堆后的最大值/最小值

a存输入数据

过程

it从1开始,先合并挨着的两个,求出最大值和最小值。i和j分别代表范围的位置

就像这样:

———[i——j]———–

k枚举 $i-k 和 k-j$ 的子区间

like this: [1,2,3] -> [(1,2),3] || [1,(2,3)] => max & min

 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
#include <iostream>
#include <cstdio>
using namespace std;
int dpmax[1001][1001], dpmin[1001][1001], a[1001], n, s[1001]; //qzh not qiezihe dp: max after combining i to j
int value(int i, int j)
{
    return s[j] - s[i - 1];
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        a[i + n] = a[i];
    }
    for (int i = 1; i <= n + n; i++)
    {
        s[i] = s[i - 1] + a[i];
    } //qzh
    for (int it = 1; it < n; it++)
    {
        for (int i = 1, j = i + it; (i <= n + n) && (j <= n + n); i++, j = i + it;)
        {
            dpmin[i][j] = 0x3f3f3f3f;
            for (int k = 1; k <= n; k++)
            {
                dpmax[i][j] = max(dpmax[i][j], dpmax[i][k] + dpmax[k + 1][j]) + value(i, j);
                dpmin[i][j] = min(dpmin[i][j], dpmin[i][k] + dpmin[k + 1][j]) + value(i, j);
            }
        }
    }
    int minl = 0x3f3f3f3f, maxl = 0;
    for (int i = 1; i <= n; i++)
    {
        maxl = max(maxl, dpmax[i][i + n - 1]);
        minl = min(minl, dpmin[i][i + n - 1]);
    }
    return 0;
}