1 条题解

  • 0
    @ 2026-10-26 13:00:02

    思路与算法

    dp[l][r]为区间合体最大收益。枚举分离点k∈[l,r-1],左右子区间收益相加,再加(a[l]+a[r])*a[k]。单区域不分离,收益0。记录最优k,遇到相同收益保留较小k。恢复时把整个区间入队,输出队首区间的分离点后,依次把左右子区间入队,直到只剩单区域。

    正确性说明

    第一次分离将问题分成相互独立的两个连续区间,最终这两个子区间合体时的额外收益仅由l,r,k决定。因而各子区间最优值可以相加,枚举k完整覆盖全部可能首次分离。按长度归纳dp最优。相同收益选较小首次分离点,使输出的第一个不同决策最小;子问题采用同样规则。队列按深度且从左到右处理,恰好实现题目要求的阶段输出顺序。

    复杂度

    时间O(n³),空间O(n²),包括收益表与决策表,初始化O(n²)。恢复阶段每节点入队一次O(n),额外队列O(n)。n≤300,原题保证运算过程与结果在32位正整数内,程序仍用long long保存收益。

    易错点

    输出顺序是层序分离过程,不能用普通递归前序输出左右子问题。k最大r-1,不能分出空区间。相同收益只在严格更优时更新,保留最靠左决策。n=1时最大收益0,第二行为空。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int n;
        cin >> n;
        vector<int> a(n);
        for (int &x : a)
            cin >> x;
        vector<vector<long long>> dp(n, vector<long long>(n));
        vector<vector<int>> cut(n, vector<int>(n));
        for (int len = 2; len <= n; ++len)
            for (int l = 0; l + len <= n; ++l) {
                int r = l + len - 1;
                for (int k = l; k < r; ++k) {
                    long long value = dp[l][k] + dp[k + 1][r] + (a[l] + a[r]) * a[k];
                    if (k == l || value > dp[l][r]) {
                        dp[l][r] = value;
                        cut[l][r] = k;
                    }
                }
            }
        cout << dp[0][n - 1] << '\n';
        queue<pair<int, int>> q;
        q.push({0, n - 1});
        bool first = true;
        while (!q.empty()) {
            auto t = q.front();
            q.pop();
            int l = t.first, r = t.second;
            if (l == r)
                continue;
            int k = cut[l][r];
            if (!first)
                cout << ' ';
            first = false;
            cout << k + 1;
            q.push({l, k});
            q.push({k + 1, r});
        }
        cout << '\n';
    }
    
    • 1

    信息

    ID
    1743
    时间
    1000ms
    内存
    512MiB
    难度
    普及
    标签
    递交数
    1
    已通过
    1
    上传者