从CF1935C看带反悔的贪心和multiset
思路
首先很显然对 \(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}\)
- 能同时维护最大值和最小值,且支持访问
- \(\texttt{std::multiset}\)
代码
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';
}