【题解】P2893 Making the Grade G

题目链接

题意

给你一个长度为 \(n\) 的序列 \(a\)

需要构造一个长度为 \(n\) 的非严格单调序列 \(b\),求出 \(\min_{i=1}^{n}\{|a_i-b_i|\}\)

思路

感觉这题还挺难的。

首先我们需要证一个莫名其妙的东西,就是 \(b_i\in a(1\le i\le n)\)

就是 \(b\) 中的元素一定在 \(a\) 中出现过。

在序列长度为 \(1\) 的时候,这个是一定满足的,毕竟取原值,答案就是 \(0\) 了啊。

然后递归证明(自己瞎起的)一下,就是从前 \(k-1\) 项满足这个定理,推导到前 \(k\) 项满足这个定理。

分讨一下。

首先是 \(b_{k-1}\le a_k\),这样直接取 \(a_k\) 就行了,这样 \(a_k\) 肯定是在 \(a\) 中的啊。

然后这个是大问题,当 \(b_{k-1}>a_k\) 时。

这样的话有两种办法,第一种是 \(b_k=b_{k-1}\),这样是满足条件的,因为 $b_{k-1} 在 \(a\) 中,所以 \(b_k\) 也在 \(a\) 中。

第二种是取一个 \(j\)\(v\),让 \(b_{j\dots k}\) 都等于 \(v\),且 \(v>b_{j-1}\)

我们要求的是啥?是 \(\min_{i=1}^{n}\{|a_i-b_i|\}\)

而在 \(l\)\(k\) 这一段区间内 \(b\) 的取值是相等的,这不就是“货库选址”吗,所以我们可以取 \(a_{l\dots k}\) 的中位数。但是如果这个中位数 \(p\le b_{l-1}\) 的话,我们的解就肯定是 \(b_{l-1}\) 了,这两种取值无论如何都是在 \(a\) 当中的。

证毕。

回归正题。

因为 \(b\) 可以是升的,也可以是降得,所以下面会讲述非严格单调单调上升,下降其实理解上升后改一改就行了,就不赘述了。

可见的是 \(n\) 的取值只有 \(2000\),所以我们可以考虑一下 dp。

我们可以设 \(f_{ij}\) 为 到了第 \(i\) 个数时,第 \(i\) 个数的取值为 \(j\),它的最小答案。

因为前面的引理,所以 \(j\) 只需要在序列 \(a\) 中便利,虽然 \(a\) 的值很大,但是我们可以离散化。

这样的话,转移还是很好搞得。

\[ f_{i,j}=|a_i-c_j|+\min_{k=1}^{n}\{f_{i-1,k}\}(c_k\le c_j) \]

\(c\) 是离散化数组,因为离散化数组都是排好序的所以,上面的式子就是:

\[ f_{i,j}=|a_i-c_j|+\min_{k=1}^{j}\{f_{i-1,k}\} \]

这样的话枚举 \(i\) 一个 \(O(n)\),枚举 \(j\) 一个 \(O(n)\),枚举 \(k\) 一个 \(O(n)\),总复杂度 \(O(n^3)\)

这个方法在 luogu 上会 TLE 一个点,成为高贵的 \(90\) 分。

细细思考一下,当你扫描过一个答案 \(i\) 时,随着 \(j\) 的增加,\(k\) 的取值范围是不断增加,且 \(k\) 的取值范围是将之前的取值范围包含进去的。所以每经过一个 \(j\),我们就可以将它增加的 \(k\) 的取值的答案加入当前答案集合,这样的话 \(j+1\) 就不用循环 \(k\) 了,以此类推,\(k\) 的那一层循环就没了。

看一下代码理解一下吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 3e3 + 5;
const ll inf = 1000000000;
ll n;
ll a[N], b[N], c[N], tot;
ll f[N][N];
int main() {
    cin >> n;
    for (ll i = 1; i <= n; i++) cin >> a[i];
    for (ll i = 1; i <= n; i++) b[i] = a[i];
    sort(b + 1, b + 1 + n);
    for (ll i = 1; i <= n; i++) {
        if (b[i] != b[i - 1] || i == 1) {
            c[++tot] = b[i];
        }
    }
    for (ll i = 1; i <= n; i++) {
        ll val = f[i - 1][1];
        for (ll j = 1; j <= tot; j++) {
            f[i][j] = abs(a[i] - c[j]) + val;
            val = min(f[i - 1][j + 1], val);
        }
    }
    ll an1 = inf;
    for (ll i = 1; i <= tot; i++) an1 = min(f[n][i], an1);
    for (ll i = 1; i <= n; i++) {
        for (ll j = 1; j <= tot; j++) {
            f[i][j] = 0;
        }
    }
    for (ll i = 1; i <= n; i++) {
        ll val = f[i - 1][tot];  // 没有改变初值,还写得上面的f[i-1][1]
        for (ll j = tot; j >= 1; j--) {
            f[i][j] = abs(a[i] - c[j]) + val;
            val = min(f[i - 1][j - 1], val);
        }
    }
    for (ll i = 1; i <= tot; i++) an1 = min(an1, f[n][i]);
    cout << an1 << '\n';
    return 0;
}
/*
15
1 8 5 10 3 12 9 6 15 7 11 2 4 13 14

2085
*/
posted @ 2025-08-20 15:46  Kcjhfqr  阅读(13)  评论(0)    收藏  举报
.poem-wrap { position: relative; width: 1000px; max-width: 80%; border: 2px solid #797979; border-top: none; text-align: center; margin: 40px auto; } .poem-left { left: 0; } .poem-right { right: 0; } .poem-border { position: absolute; height: 2px; width: 27%; background-color: #797979; } .poem-wrap p { width: 70%; margin: auto; line-height: 30px; color: #797979; } .poem-wrap h1 { position: relative; margin-top: -20px; display: inline-block; letter-spacing: 4px; color: #797979; font-size: 2em; margin-bottom: 20px; } #poem_sentence { font-size: 25px; } #poem_info { font-size: 15px; margin: 15px auto; }