ACWING 273 分级

https://www.acwing.com/problem/content/275/

题意

给定一个序列 A A A,要求构造一个序列 B B B使得

  1. B B B非严格单调,也就是允许取等的上升或下降序列
  2. 最小化 S = ∑ i = 1 N ∣ A i − B i ∣ S=\sum_{i=1}^{N}|A_i-B_i| S=i=1NAiBi
    输出S

思路

该问题具有这样一条性质:

B必然可由A中元素构成得到最优解

我们假设现在已有最优解 B B B,但 B B B中元素尚不是由 A A A中元素构成。则我们任取其中位于 A i A_i Ai A i + 1 A_{i+1} Ai+1之间的一些元素,并记 B B B中小于等于 A i A_i Ai的元素有 X X X个,大于等于 A i + 1 A_{i+1} Ai+1的元素有 Y Y Y个。则

  1. X < Y X<Y X<Y时,必然可以通过将选取元素中最大者调整为 A i + 1 A_{i+1} Ai+1的方式得到更优结果。
  2. X > Y X>Y X>Y时,必然可以通过将选取元素中最小者调整为 A i A_{i} Ai的方式得到更优结果。
  3. X = Y X=Y X=Y时,可以任意向上或向下调整,结果不会变差。

由此,我们知道可以通过取A中的元素并重新排列得到B的最优解。

接下来,我们设计状态转移方程部分

f[i][j]代表给A[1]~A[i]分配好了值且最后一个数是A'[j](第j大的数)的方案的集合,值是集合中所有方案的最小值(最优)。

从倒数第二个分配的数转移,有

倒数第二个数选取的是A'[1],则最小值是f[i-1][1]+abs(A[i]-A'[1])

倒数第二个数选取的是A'[2],则最小值是f[i-1][2]+abs(A[i]-A'[2])

以此类推,最后取最小值即可。

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

const int N=2010;
const int INF=0x3f3f3f3f;

typedef long long ll;
ll a[N],b[N],f[N][N];
int n;

ll work(){
    memcpy(b, a, sizeof b);
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++){
        ll 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]);
        } 
    }
    ll 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];    
    ll res=work();
    reverse(a+1,a+n+1);
    res=min(res,work());
    cout<<res<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值