题目描述
给定一个长度为 n (
你每次可以花费 1 的代价给某个 a[i] (
求最少需要多少代价能将这个序列变成一个不上升序列。
题解
首先想到的是最朴素的DP:
dp[i][j]表示前i个数排成不上升序列,最小的数为j,所需的最小代价;
dp[1][j]=|j-a[1]|
枚举k,(k>=a[i])
dp[i+1][j]=min(dp[i][k])+|j-a[i]|;
令函数
由于每次转移时,总是取大于等于某个值的区域里的最小值,所以左半边递减的图像没有任何用处,直接忽略掉:
此时,
还可以发现,这个函数的斜率一定是整数,当x增加1时,相当于数组a至少1个的元素需要更改值,花费自然也是整数,所以斜率也就为整数。
仔细观察,还可以知道,斜率一定是不下降的:当x每增加1,数组a中需要更改的元素个数不可能比以前少,所以增加的代价一定比x小的时候多,斜率也就不下降了。
继续研究转移:
令函数
函数
令
如果
如果
同样,图上
实现
实现时,使用一个优先队列(堆)来存储当前
用ans表示此时
每添加一个新的a[i],获取p (
如果
只需将a[i]插入堆中即可
如果
最后从1~n枚举i,最后的ans即为答案
代码
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;
priority_queue< int,vector<int>,greater<int> > Q;
int main()
{
int n,a;
long long ans=0LL;
scanf("%d",&n);
scanf("%d",&a);
Q.push(a);
for(int i=2;i<=n;i++)
{
scanf("%d",&a);
Q.push(a);
int mn=Q.top();
if(mn<a)
{
Q.pop();
ans+=1LL*(a-mn);
Q.push(a);
}
}
printf("%lld\n",ans);
return 0;
}