最长非严格上升子序列的思考 && 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 = lower_bound(f + 1, f + 1 + n, a[i]) - f;
        cout << a[i] << ' ' << cur << '\n';
        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 << '\n';
        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 << '\n';
        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 << '\n' << f[n] << '\n';
    return 0;
}
posted @ 2020-11-05 09:27  洛绫璃  阅读(202)  评论(0编辑  收藏  举报