12 ACwing 282 石子合并 题解

石子合并

题面

设有 N 堆石子排成一排,其编号为 1,2,3,…,N。

每堆石子有一定的质量 \(a_i\) ,可以用一个整数来描述,现在要将这 N 堆石子合并成为一堆。

每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

\(1 \le N \le 300\)

\(0 \le a_i \le 1000\)

题解

这是区间 dp 板子,因为合并时只能合并相邻的两堆石子,所以每堆石子都可以用一个闭区间 \([l,r]\) 表示,而这个闭区间可以从 \([l,k]\)\([k + 1, r]\) 两个子区间转移过来,所以我们可以将区间长度 \(len\) 作为 “阶段” ,左端点和右端点就是状态,中间的 \(k\) 就是 “决策”

\(f(l,r)\) 为将 \([l,r]\) 合并为一堆石子的最小代价

转移 \(f(l,r) = min \{f(l,k) + f(k + 1, r)\} + sum(l,r)\)

code

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

using namespace std;

typedef long long ll;

const int N = 310;

int n;
int a[N], sum[N], f[N][N];

int main () {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum[i] = a[i] + sum[i - 1];
    }

    memset (f, 0x3f, sizeof f);

    for (int i = 1; i <= n; i++) {
        f[i][i] = 0;
    }

    for (int len = 2; len <= n; len ++) {
        for (int l = 1; l + len - 1 <= n; l ++) {
            int r = l + len - 1;
            for (int k = l; k < r; k ++) {
                f[l][r] = min (f[l][r], f[l][k] + f[k + 1][r]);
            }
            f[l][r] += sum[r] - sum[l - 1];
        }
    }
    
    cout << f[1][n] << endl;
    return 0;
}
posted @ 2025-10-05 18:01  michaele  阅读(6)  评论(0)    收藏  举报