3 ACwing 273 Making the Grade 题解

ACwing 273 Making the Grade

题面

给定长度为 \(N\) 的序列 \(A\) ,构造一个长度为 \(N\) 的序列 \(B\) ,满足

  • \(B\) 非严格单调
  • \(S = \sum_{i = 1}^N |A_i - B_i|\) 最小

求出最小值 \(S\)

\(1 \le N \le 2000\)

\(0 \le A_i \le 10^6\)

题解

先证明一个引理:\(B\) 中的所有数都在 \(A\) 中出现过

我们利用数学归纳法进行证明

首先对于 \(k + 1 = 1\) 的情况,显然成立

对于 \(k + 1 > 1\) 的情况

  • \(A_{k + 1} \ge B_k\) 我们取 \(B_{k + 1} = A_{k + 1}\) 即可

  • 否则,要么 \(B_{k + 1} = B_k\) ,要么存在 \(x\) 使得 \(B_j...B_{k + 1} = x\)

    \(mid\)\(A_j...A_{k + 1}\) 的中位数,根据货仓选址一题,如果 \(mid \ge B_{j - 1}\) \(x = mid\) ,否则 \(x = B_{j - 1}\)

对于以上情况,涵盖了最优解的同时也保证了 \(B\) 中的所有数都在 \(A\) 中出现过

所以我们就证明了引理

因为这道题的限制为序列递增,所以我们需要知道序列中数的大小关系

\(f(i,j)\) 表示考虑前 \(i\) 个数 \(B_i = j\) 的情况下的最小代价,初始 \(f(0,i) = 0\) ,目标状态为 \(\min \{f(n,i)\}\)

转移:\(f(i,j) = \min_{0 \le k \le j} \{f(i - 1, k) + |A_i - j|\}\)

这个候选集合也是只增不减的,可以用一个变量维护,将 \(A\) 离散化后,时间复杂度 \(O(N^2)\)

code

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

using namespace std;

const int N = 2e3 + 10;
const int INF = 2147483647;

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

int work () {
    int res = INF;
    memset (f, 0x3f, sizeof f);
    for (int i = 1; i <= n; i++) f[0][i] = 0;
    for (int i = 1; i <= n; i++) {
        int mn = INF;
        for (int j = 1; j <= n; j++) {
            mn = min (mn, f[i - 1][j]);
            f[i][j] = mn + abs (a[i] - num[j]);
        }
    }
    for (int i = 1; i <= n; i++) {
        res = min (res, f[n][i]);
    }
    return res;
}

int main () {
    cin >> n;
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        num[i] = a[i];
    }
    //num[i] 表示排名第 i 个的 a_i
    sort (num + 1, num + 1 + n);

    int ans = work ();

    //反转后,相当于在求递减的 b
    reverse (a + 1, a + 1 + n);
    ans = min (ans, work ());

    cout << ans << endl;
    
    return 0;
}
posted @ 2025-10-05 17:59  michaele  阅读(13)  评论(0)    收藏  举报