47 SP估算Estimation 题解

Estimation

题面

给定一个长度为 N 的整数数组 A,你需要创建另一个长度为 N 的整数数组 B,数组 B 被分为 M 个连续的部分,并且如果 i 和 j 在同一个部分,则 \(B[i]=B[j]\)

如果要求数组 B 能够满足 \(\sum |A_i - B_i|\) 最小,那么最小值是多少,请你输出这个最小值。

\(1 \le N \le 2000, 1 \le M \le \min(N,25)\)

题解

这道题如果你知道怎么动态求第 \(k\) 大,那么应该就比较简单了

\(f(i,j)\) 表示将前 \(i\) 个数,分成 \(j\) 段的最小代价

有转移

\[f(i,j) = \min_{0 \le k \le i - 1} \{ f(k,j - 1) + cost(k + 1, j) \} \]

如果能够预处理 \(cost\) 那么这个转移就是 \(O(N^2M)\) 的,可以接受

想办法预处理 \(cost\) ,这个可以联想一下“货仓选址”,和那个是一样的,所以我们需要求出 \(i \sim j\) 的中位数,暴力求是 \(O(N \log N)\) 的,带上外层循环,显然超时

所以这里要用到一个动态求第 \(k\) 大的技巧,也就是用对顶堆维护第 \(k\)

可以在 \(O(N^2 \log N)\) 的时间复杂度内预处理出 \(cost\)

然后就是这道题有点卡常,优先队列的快速清空可以用 .swap(tmp)

code

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

using namespace std;

const int N = 2e3 + 10, M = 30;

int n, m, f[N][M];
int a[N], val[N][N], cost[N][N];
unordered_map <int, int> mp;

int rd () {
    int res = 0, f = 1;
    char c = getchar ();
    while (c < '0' || c > '9') {
        if (c == '-') f = -1;
        c = getchar ();
    }
    while (c >= '0' && c <= '9') {
        res = (res << 1) + (res << 3) + (c ^ 48);
        c = getchar ();
    }
    return res * f;
}

struct heap_pair {
    int cnt;
    priority_queue <int, vector <int>, greater<int> > mn;
    priority_queue <int> ma;
    void clear () {
        priority_queue <int, vector <int>, greater<int> > mn2;
        priority_queue <int> ma2;
        mn.swap (mn2), ma.swap (ma2);
        cnt = 0;
    }
    void push (int x) {
        if (ma.empty () || x <= ma.top ()) ma.push (x);
        else mn.push (x);
        cnt ++;
        int k = (cnt + 1) >> 1;
        if ((int)ma.size () < k) {
            ma.push (mn.top ());
            mn.pop ();
        }
        if ((int)ma.size () > k) {
            mn.push (ma.top ());
            ma.pop ();
        }
    }
} q;


int main () {

    while (n = rd (), m = rd ()) {
        if (!n && !m) break;
        q.clear ();

        for (int i = 1; i <= n; i ++) {
            a[i] = rd();
            mp[a[i]] = i;
        }

        //预处理val, val[i][j] 表示 [1~j abs (a[j] - a[i])]
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= n; j ++) {
                val[i][j] = abs (a[j] - a[i]) + val[i][j - 1];
            }
        }
        //预处理cost, cost[i][j] 表示将 i~j 分为一段的最小代价
        for (int i = 1; i <= n; i ++) {
            q.clear ();
            for (int j = i; j <= n; j ++) {
                q.push (a[j]);
                int mid = q.ma.top ();
                int pos = mp[mid];
                cost[i][j] = val[pos][j] - val[pos][i - 1];
            }
        }


        memset (f, 0x3f, sizeof f);
        f[0][0] = 0;

        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= min (i, m); j ++) {
                for (int k = i - 1; k >= 0; k --) {
                    f[i][j] = min (f[i][j], f[k][j - 1] + cost[k + 1][i]);
                }
            }
        }

        printf ("%d\n", f[n][m]);
    }

    return 0;
}
posted @ 2025-10-09 21:21  michaele  阅读(7)  评论(0)    收藏  举报