1. 程式人生 > >hihocoder #1529 : 不上升序列

hihocoder #1529 : 不上升序列

表示 ret pre pop 需要 存在 scrip esc int

Description

給定一個長度為 n 的非負整數序列 a[1..n]。
你每次可以花費 1 的代價給某個 a[i] 加1或者減1。
求最少需要多少代價能將這個序列變成一個不上升序列。

Solution

容易想到一個 \(dp\),設 \(f[x][i]\) 表示前 \(x\) 個數,最小的數不小於 \(i\) 的最小代價
\(f[x][i]=f[x-1][i]+|a_x-i|\)
其實這是兩條折線合並的過程,\(|a_x-i|\) 是一條以 \(a_x\) 為拐點的折線,且兩條直線的斜率分別為 \(1,-1\)
對於 \(f[x][i]\) 也是一條折線或直線,顯然在拐點處的解是最優的(也就是斜率變成 \(0\)

的位置,折線是不會穿過 \(x\) 軸的,所以肯定存在斜率為 \(0\) 的位置)

我們分兩種情況合並這兩條折線
技術分享圖片
觀察圖片發現:新圖也就是把原圖中的兩條折線的每一段的斜率分別相加

這裏的拐點也就是使得斜率發生改變的地方,因為斜率發現改變的地方都有可能成為斜率為 \(0\) 的那個拐點
用堆維護拐點的橫坐標就可以了

#include <bits/stdc++.h>
using namespace std;
const int N=5e5+10;
priority_queue<int>Q;
int main()
{
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    int n,x;long long ans=0;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",&x);
        x=-x;Q.push(x);
        if(Q.top()>x)ans+=Q.top()-x,Q.pop(),Q.push(x);
    }
    printf("%lld\n",ans);
    return 0;
}

hihocoder #1529 : 不上升序列