其他分享
首页 > 其他分享> > 「REMAKE系列」线性dp篇

「REMAKE系列」线性dp篇

作者:互联网

常见模型、技巧总结

LIS、LCS模型

LIS

习题

洛谷——「能力综合提升题单-线性DP篇」

P2501 [HAOI2006]数字序列

省选/NOI- LIS、结论

现在我们有一个长度为 \(n\) 的整数序列 \(a\)。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。
第一行输出一个整数,表示最少需要改变多少个数。
第二行输出一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

思路

const int N = 4e4 + 10;
int a[N], n, b[N], v[N];
ll dp[N];
ll s[N], suf[N], L[N];
vector<int> pos[N];

int main() {
    re(n);
    for (int i = 1; i <= n; i++)
        re(a[i]), b[i] = a[i] - i;
    b[0] = -2e9;
    b[n + 1] = 2e9;     // 最后一个(段)元素也是会被修改的,所以将边界搞到 n + 1
    int len = 0;
    for (int i = 1; i <= n + 1; i++) {
        int l = 0, r = len + 1;
        while (l < r) {
            int mid = (l + r) >> 1;
            if (b[v[mid]] > b[i]) r = mid;
            else l = mid + 1;
        }
        if (l == len + 1) v[++len] = i;
        else v[l] = i;
        L[i] = l;
        pos[l].pb(i);
    }
    int ans1 = n - len + 1;
    memset(dp, 0x3f, sizeof dp);
    dp[0] = 0;
    pos[0].pb(0);
    for (int i = 1; i <= n + 1; i++) {
        for (auto pre: pos[L[i] - 1]) {
            if (pre > i || b[pre] > b[i]) continue;
            s[pre] = 0;
            suf[i - 1] = 0;
            for (int j = pre + 1; j <= i - 1; j++) {
                s[j] = s[j - 1] + abs(b[j] - b[pre]);
            }
            for (int j = i - 2; j >= pre; j--)
                suf[j] = suf[j + 1] + abs(b[j + 1] - b[i]);
            for (int k = pre; k <= i - 1; k++) {    // pre, k --- k + 1, i
                dp[i] = min(dp[i], dp[pre] + s[k] + suf[k]);
            }
        }
    }
    printf("%lld\n%lld\n", ans1, dp[n + 1]);
    return 0;
}

标签:pre,int,LIS,leq,序列,REMAKE,线性,dp
来源: https://www.cnblogs.com/Roshin/p/remake_linear_dp.html