ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

「REMAKE系列」线性dp篇

2022-09-12 22:32:07  阅读:197  来源: 互联网

标签:pre int LIS leq 序列 REMAKE 线性 dp


常见模型、技巧总结

LIS、LCS模型

LIS

习题

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

P2501 [HAOI2006]数字序列

省选/NOI- LIS、结论

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

  • 对于 \(100\%\) 的数据,保证 \(1 \leq n \leq 3.5 \times 10^4\),\(1 \leq a_i \leq 10^5\)。数据保证 \(a_i\) 随机生成。

思路

  • 结论题两个结论,数据随机 \(O(n^2)\) 可以过。
  • 对于第一问,构造 \(b[i] = a[i] - i\),求 b[] 的最长单调不减子序列长度即可。
  • 对于第二问。考虑 b[] 中最长单调不减子序列中的相邻元素下标为 i, j,对应下标之间的大小一定不在 [b[i], b[j]] 之间,否则子序列可以更长。
    • 那么对于每一对相邻元素(序列不止一个),最终的结果一定是中间某一个 \(k(i\leq k \leq j)\), 使得 b[i..k] = b[i], b[k+1...j]=b[j],这样的结果是最优的。
    • 所以对于每一对相邻元素的区间,采用 dp 转移,设 dp[i] 表示处理以下标 \(i\) 为结尾的最长单调不减子序列最小代价
    • 枚举分界点 k。判断相邻元素区间合法,在 LIS 时记录每个下标作为末尾对应的最长长度。
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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有