【题解】AcWing 273.分级

AcWing 273.分级

题目描述

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

  1. B B B 非严格单调,即 B 1 ≤ B 2 ≤ … ≤ B N B_1≤B_2≤…≤B_N B1B2BN B 1 ≥ B 2 ≥ … ≥ B N B_1≥B_2≥…≥B_N B1B2BN
  2. 最小化 S = ∑ i = 1 N ∣ A i − B i ∣ S=∑^N_{i=1}|A_i−B_i| S=i=1NAiBi

只需要求出这个最小值 S S S

输入格式

第一行包含一个整数 N N N

接下来 N N N 行,每行包含一个整数 A i A_i Ai

输出格式

输出一个整数,表示最小 S S S 值。

数据范围

1 ≤ N ≤ 2000 1≤N≤2000 1N2000,
0 ≤ A i ≤ 1 0 6 0≤A_i≤10^6 0Ai106

输入样例

7
1
3
2
4
5
3
9

输出样例

3

题目分析

引理:在最优情况下,一定存在一种构造序列 B B B 的方式,使得 B B B 中每个元素都在 A A A 中出现过。

用数学归纳法证明:
N = 1 N=1 N=1 时显然成立;
假设当 N = k N=k N=k 时上述命题成立,那么当 N = k + 1 N=k+1 N=k+1时:

  1. B k ≤ A k + 1 B_k\le A_{k+1} BkAk+1,那么令 B k + 1 = A k + 1 B_{k+1}=A_{k+1} Bk+1=Ak+1
  2. 否则要么令 B k + 1 = B k B_{k+1}=B_k Bk+1=Bk
  3. 要么 ∃ j ≤ k , B j = B j + 1 = … = B k = B k + 1 = v \exist j\le k,B_j=B_{j+1}=…=B_{k}=B_{k+1}=v jk,Bj=Bj+1==Bk=Bk+1=v。此时设 A j , A j + 1 , … , A k + 1 A_j,A_{j+1},…,A_{k+1} Aj,Aj+1,,Ak+1 的中位数为 m i d mid mid,若 m i d ≥ B j − 1 mid\ge B_{j-1} midBj1,那么 v = m i d v=mid v=mid,否则 v = B j − 1 v=B_{j-1} v=Bj1

无论哪种情况, B B B 中所有数都在 A A A 中出现过。

状态表示:令 A A A 经过排序后组成的序列为 A ′ A' A。设 f [ i , j ] f[i,j] f[i,j] 表示已经完成 B B B 中前 i i i 个数的构造,且 B [ i ] = A ′ [ j ] B[i]=A'[j] B[i]=A[j] S S S 的最小值。

扫描二维码关注公众号,回复: 13393841 查看本文章

状态计算:根据倒数第二个数分类。 f [ i , j ] = m i n { f [ i − 1 , k ] + ∣ A [ i ] − A ′ [ j ] ∣ ∣ 0 ≤ k ≤ j } f[i,j]=min\{f[i-1,k]+|A[i]-A'[j]||0\le k\le j\} f[i,j]=min{ f[i1,k]+A[i]A[j]0kj}

类似 最长公共上升子序列,我们用一个数记录 f [ i − 1 , k ] f[i-1,k] f[i1,k] 的最小值,因此只需两重循环。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

const int INF = 1e9;

int n, res = INF;
int a[2010], b[2010], f[2010][2010];

int dp(){
    
    
    for (int i = 1; i <= n; i ++) b[i] = a[i];
    sort(b + 1, b + n + 1);
    
    for (int i = 1; i <= n; i ++){
    
    
        int minv = INF;
        for (int j = 1; j <= n; j ++){
    
    
            minv = min(minv, f[i - 1][j]);
            f[i][j] = minv + abs(a[i] - b[j]);
        }
    }
    int res = INF;
    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];
    res = dp();
    reverse(a + 1, a + n + 1);
    res = min(res, dp());
    cout << res;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/f4u4u4r/article/details/121278776
今日推荐