【数学?】hihocoder1529 不上升序列

题目描述:

给定一个长度为 n 的非负整数序列 a[1..n]。

你每次可以花费 1 的代价给某个 a[i] 加1或者减1。

求最少需要多少代价能将这个序列变成一个不上升序列。


分析:

我们设DP(i,j)表示前i个数已经满足不上升的情况下,第i个数为j的最小代价
转移式很好想:
DP(i,j)=min(DP(i1,k))(kj)+|aij| D P ( i , j ) = m i n ( D P ( i − 1 , k ) ) ( k ≥ j ) + | a i − j |
显然我们答案就是 min(DP(n,i)0i) m i n ( D P ( n , i ) 0 ≤ i ) 当然,我们不可能这样去裸跑DP的(时空均炸完)
我们来分析一下这个函数的图像:
DP(1)://即第一维固定,下图中的横坐标表示第二维的参数,纵坐标表示函数值
这里写图片描述
这个图像很显然,但并没有很明显的特征,我们继续研究DP(2):
首先我们考虑 min(DP(i1,k))(kj) m i n ( D P ( i − 1 , k ) ) ( k ≥ j ) 的取值:
这里写图片描述
很显然是这样一个样子的。
在这个基础上,我们再套上一个和 DP(1) D P ( 1 ) 一样的图像,就会出现两种情况:
这里写图片描述
以及:
这里写图片描述
其实就是根据A1和A2的大小来区分的。
到这里,你应该也看出来一些特征了:
这个图像无论经过多少次变化,一定会是一个单谷图像
现在,我们将一个转折点右边的线的斜率作为这个点的权值。

再分析每加入一个点后的变化:
每次 min(DP(i1,k))(kj) m i n ( D P ( i − 1 , k ) ) ( k ≥ j ) 的图像必然是一个递增的图像,因此,所有权值小于等于0的点都会被消除,在加入一个新的折线后,折线零点(即 Ai A i 的值)左边的所有点权值-1,右边的所有点权值+1。并且当前零点的权值也要+1。

结合这一点看,我们每次消除的点最多只有1个,即使没有消除点,那么最左边的点的权值必然也会-1,可以看做在同一个位置上有多个点,删去其中一个。

这样一来就很简单了,我们可以用优先队列存储每个点的位置,每次加入一个新点,可以在当前放两个点,再从最左边删去一个点,而我们的代价,就是删去的点和加入的点的距离。
所以代码相当的短:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define SF scanf
#define PF printf
#define MAXN 1000010
using namespace std;
int n;
long long ans,x;
priority_queue<long long,vector<long long>,greater<long long> > q;
int main(){
    SF("%d",&n);
    for(int i=1;i<=n;i++){
        SF("%lld",&x);
        q.push(x);
        if(q.top()<x){
            ans+=x-q.top();
            q.pop();
            q.push(x);
        }
    }
    PF("%lld",ans);
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值