• 最长非严格上升子序列的思考 && CF 1437E Make It Increasing


    最长严格上升子序列

    这就是基础的线性dp了(咱们就不讨论 平方暴力做法了)

    int main() {
        IOS; cin >> n;
        rep (i, 1, n) f[i] = 1e9;
        rep (i, 1, n) {
            cin >> a[i];
            int cur = upper_bound(f + 1, f + 1 + n, a[i]) - f;
            cout << a[i] << ' ' << cur << '
    ';
            f[cur] = a[i]; umax(ans, cur);
        }
        cout << ans;
        return 0;
    }
    

    最少改几个数,可以使 a[i] 非严格上升

    那不就是长度减去 非严格上升子序列的长度吗?

    int main() {
        IOS; cin >> n;
        rep (i, 1, n) f[i] = 1e9;
        rep (i, 1, n) {
            cin >> a[i];
            int cur = upper_bound(f + 1, f + 1 + n, a[i]) - f;
            cout << a[i] << ' ' << cur << '
    ';
            f[cur] = a[i]; umax(ans, cur);
        }
        cout << n - ans;
        return 0;
    }
    

    最少改几个数, 可以使得 a[i] 严格上升

    想想和上面的联系,严格和非严格 有啥区别?

    a[i] < a[i + 1] 和 a[i] <= a[i + 1]

    我们默认先让 a[i+1] 比 a[i] 小 1 , a[i] < a[i + 1] 不就成了 a[i] <= a[i + 1] 了吗?

    (现 a[i+1] = 原 a[i+1] - 1), 每个数都这样, 不就是 a[i] - i 吗?

    即 1~n a[i] -= i, 就成了上个问题

    int main() {
        IOS; cin >> n;
        rep (i, 1, n) f[i] = 1e9;
        rep (i, 1, n) {
            cin >> a[i]; a[i] -= i;
            int cur = upper_bound(f + 1, f + 1 + n, a[i]) - f;
            cout << a[i] << ' ' << cur << '
    ';
            f[cur] = a[i]; umax(ans, cur);
        }
        cout << n - ans;
        return 0;
    }
    

    CF 1437E Make It Increasing

    主菜来了, 先把不能找出来的直接 return 掉

    什么情况不行呢? 首先是不然改的序列存在 a[b[i]] > a[b[i + 1]], 或者不让改的两个数之间放不下 a[b[i+ 1]] - a[b[i]]

    然后是怎么处理 不能改的位置 (我就到这步卡住的)

    其实我们注意到, 只有被选入 f[i] 才有可能不被修改, 当然 f[ans] 肯定是不会被修改的

    怎么让 f[i] 也不被修改呢? 每次替换只能替换 当前最近的不能修改的位置之后的数就好了 (没想到这个, 我是sb)

    int main() {
        IOS; cin >> n >> m; f[0] = -2e9;
        rep(i, 1, n) cin >> a[i];
        rep(i, 1, m) {
            cin >> b[i]; v[b[i]] = 1;
            if (i == 1) continue;
            if (b[i] < b[i - 1] || a[b[i]] - a[b[i - 1]] < b[i] - b[i - 1])
                cout << -1, exit(0);
        }
    
        rep(i, 1, n) {
            a[i] -= i;
            if (f[ans] <= a[i]) {
                f[++ans] = a[i];
                if (v[i]) k = ans;
            }
            else {
                int cur = upper_bound(f + 1, f + 1 + ans, a[i]) - f;
                if (cur <= k) continue;
                f[cur] = a[i];
                if (v[i]) k = ans = cur;
            }
        }
        cout << n - ans;
        return 0;
    }
    

    在修改最少的的基础上花费最少

    在最少的修改数上, 想想如何最小花费

    对于 a[j](a[j] -= j) < a[i], 那么对于 a[i] 来说 a[j] 是不需要修改的

    我们何不设 dj 表示以 j 结尾变成严格上升的最小花费, 那么对于 f[i]

    (我们再设 c[i] 表示 以 i 结尾的最长上升子序列)

    则 f[i] 只考虑从 c[j] + 1 == c[i] 转移就行了

    (当然要注意 j < i 且 a[j] < a[i])

    那我们只需要考虑 j + 1 ~ i - 1 之间的数, 显然 a[j+1~i-1] 要么 > a[i], 要么 < a[j], 不然最长上升子序列可以变长了

    肯定是存在一个 k (j+1~i-1) 使得 j+1~k 全部花费 abs(a[j] - a[k]), k+1~i-1 花费 abs(a[i] - a[k]), 使得 f[i]最小

    那答案在哪里呢? 你的最长上升子序列的右端点又不一定完全包住了 1~n, 那我们人为添加一个 a[n+1] = inf 不就好了!

    ll a[N], f[N], d[N], c[N];
    ll l[N], r[N];
    
    int main() {
        IOS; cin >> n; int len = 0; b[0] = a[0] = -2e15;
        rep (i, 1, n) {
            cin >> a[i], a[i] -= i;
            if (d[len] <= a[i]) d[c[i] = ++len] = a[i];
            else {
                int cur = upper_bound(d, d + 1 + len, a[i]) - d;
                d[c[i] = cur] = a[i];
            }
        }
        ++n; d[c[n] = ++len] = a[n] = 2e15;
        vector<VI> h(len + 1);
        rep (i, 0, n) h[c[i]].pb(i);
        rep (i, 1, n) {
            f[i] = 1e18;
            for (auto j : h[c[i] - 1]) {
                if (j > i || a[j] > a[i]) continue; //可能存在 j>k>i,k顶替了i,使得j可以拼接上
                l[j] = r[i] = 0;
                rep (k, j + 1, i - 1) l[k] = l[k - 1] + abs(a[k] - a[j]);
                per (k, i - 1, j + 1) r[k] = r[k + 1] + abs(a[k] - a[i]);
                rep (k, j, i - 1) umin(f[i], f[j] + l[k] + r[k + 1]);
                
            }
        }
        cout << n - len << '
    ' << f[n] << '
    ';
        return 0;
    }
    
  • 相关阅读:
    Linux文件管理系统
    添加硬盘分区
    SWPFILE实现(增加swap空间)
    磁盘配额
    javascript插件uploadify简单实现文件上传
    Java继承--子类的实例化过程
    Java继承--子父类中的构造函数
    Java继承--覆盖
    设计模式——单例设计模式
    在vim中,使用可视化拷贝(剪切)粘贴文本
  • 原文地址:https://www.cnblogs.com/2aptx4869/p/13899757.html
Copyright © 2020-2023  润新知