Loading

CF2106D 官方题解 + 笔记

CF2106D 官方题解

有一种贪心策略。每当你在数组 \(a\) 中看到一朵花,如果它的美丽值不小于你在数组 \(b\) 中接下来必须采摘的花的美丽值,你就会采摘它。如果我们不向 \(a\) 中插入新花,同时依然能用贪心策略采集到 \(b\) 中所有的 \(m\) 朵花,那么答案就是 \(0\)

现在,考虑一下如果我们在 \(a\) 中插入一个新的美丽值为 \(k\) 的花会怎样:这将允许你“跳过” \(b\) 中的某些花,因为你可以把插入的新花放在 \(a\) 中的任意位置,并在必要时采摘它。因此,与其考虑插入新元素,我们可以将问题重新表述为从 \(b\) 中删除某个元素。一种朴素的思路是,尝试删除 \(b_1\),然后在 \(a\) 上运行贪心算法,对 \(b_2, b_3, ..., b_m\) 继续重复执行。接着,我们保留在成功删除某个花后贪心能完成采集时,对应的最小 \(b_i\)

#include <bits/stdc++.h>
using namespace std;

int t;
int n, m;

const int INF = 1e9 + 1;

bool greedy(int skip, const vector<int> &a, const vector<int> &b) {
    // cout << "开始贪心跳过第 " << skip << " 朵花\n";
    int index_b = 0;
    if (index_b == skip) {
        index_b++;
    }
    for (auto x : a) {
        if (x >= b[index_b]) {
            index_b++;
            if (index_b == skip) {
                index_b++;
            }
        }
        if (index_b >= m) {
            // cout << "贪心成功\n";
            return true;
        }
    }
    // cout << "贪心失败\n";
    return false;
}

void solve() {
    cin >> n >> m;
    vector<int> a(n), b(m);
    for (auto &x : a) {
        cin >> x;
    }
    for (auto &x : b) {
        cin >> x;
    }
    if (greedy(-1, a, b)) {
        // 不插入花可以完成
        cout << 0;
        return;
    }
    // 不插入花不能完成
    int ans = INF;
    for (int index_skip = 0; index_skip < m; index_skip++) {
        if (greedy(index_skip, a, b)) {
            // 跳过下标为 index_skip 的花可以完成
            ans = min(ans, b[index_skip]);
        }
    }
    if (ans == INF) {
        cout << -1;
        return;
    }
    cout << ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> t;
    while (t--) {
        solve();
        cout << '\n';
    }

    return 0;
}

复杂度 \(O(nm)\) 会 TLE。

优化

现在需要把这部分优化掉:

for (int index_skip = 0; index_skip < m; index_skip++) {
    if (greedy(index_skip, a, b)) {
        // 跳过下标为 index_skip 的花可以完成
        ans = min(ans, b[index_skip]);
    }
}

有没有一种办法,可以线性地判断能否删除花 \(b_i\)

如果花 \(b_i\) 可以被删除,意味着 \(b_i\) 之前的所有花和 \(b_i\) 之后的所有花都可以被 \(a\) 序列满足。对每一朵花 \(b_i\),找到最短的满足 \(b_1, b_2, ..., b_i\)\(a_1, a_2, ..., a_{p_i}\),最短的满足 \(b_i, b_{i+1}, ..., b_m\)\(a_{s_i}, a_{s_i+1}, ..., a_n\) 。如果花 \(b_i\) 可以被删除,那么花 \(b_{i-1}\) 的前缀 \(a\) 序列一定和花 \(b_{i+1}\) 的后缀 \(a\) 序列没有重叠,也就是满足 \(p_{i-1}\ <\ s_{i+1}\)。如果重叠,说明去掉花 \(b_i\) 后,剩余的部分仍不能被 \(a\) 序列满足,花 \(b_i\) 无法被删除。

双指针可以线性地预处理前缀和后缀。

最终代码

posted @ 2025-06-01 06:16  BaguetteShimada  阅读(20)  评论(0)    收藏  举报