从CF1935C看带反悔的贪心和multiset

Problem - C - Codeforces.

思路

首先很显然对 \(b\) 数组排序能最小化 \(b\) 的花费。

难点在 \(a\) 的选择,因为已经对 \(b\) 排序,不可能再兼顾 \(a\) 的优劣,所以 \(a\) 需要类似枚举的技术,这是一个类似搜索最优子集的问题,可以用 \(DP\),但是更可以贪心

带反悔的贪心

这类问题就是尽可能的取最优的,一旦发现不符合条件了,就反悔,把已选择中的最劣的逐出方案。

这题是可以这样做的,因为 \(b\) 排序之后,对于每个要枚举的起点 \(L\),哪怕把其中一个方案逐出了,也只要减去这个方案的 \(a\) 值即可,因为 \(b\) 值恒等于 \(R - L\) 的差。

针对本题,对于每一个 \(L\) 起点,都不停的选后方的 \(a\) ,一旦不符合条件,就把已选择的最大的 \(a\) 逐出方案。

实现

  • 我们需要一个数据结构来维护 \(a\) 的最大值,支持删除插入。
    • \(\texttt{std::multiset}\)
      • extract
        • 删除,且迭代器不失效
      • *rebegin()
        • 取最大
      • 相较于\(\texttt{std::priority_queue}\)
        • 能同时维护最大值和最小值,且支持访问

代码

struct Message {
    int a, b;
};
void solve() {
    int n, l;
    std::cin >> n >> l;
    std::vector<Message> arr(n);
    for (int i = 0; i < n; i++) std::cin >> arr[i].a >> arr[i].b;
    std::sort(all(arr), [&](auto& x1, auto& x2) {
        if (x1.b == x2.b) return x1.a < x2.a;
        return x1.b < x2.b;
    });
    int ans(0);
    for (int L = 0; L < n; L++) {
        std::multiset<int> S;
        int cur(0);
        for (int R = L; R < n; R++) {
            S.insert(arr[R].a);
            cur += arr[R].a;
            while(!S.empty() and arr[R].b - arr[L].b + cur > l) {//一旦不符合条件,就反悔
                int max_value(*S.rbegin());
                cur -= max_value;
                S.extract(max_value);//精确删除元素,避免迭代器失效
            }
            ans = std::max(ans, sz(S));//维护出以L起点取到的最大值
        }
    }
    std::cout << ans << '\n';
}
posted @ 2024-03-06 11:43  加固文明幻景  阅读(27)  评论(0编辑  收藏  举报