第六届辽宁省大学生程序设计竞赛 M题思路分享(贪心)
题意概述
\(A\) 有实力值 \(x\),初始为 \(0\)。有 \(n\) 场比赛,每场比赛有难度 \(a_i\) 和 隐藏分 \(b_i\)。\(A\) 只会打难度 \(a_i \ge x\) 的比赛,之后他的实力值变成 \(\max(x,b_i)\)。
通过调整比赛的顺序,计算 \(A\) 最多能打多少场比赛。
\(1\le n \le 10^6\)。
思路
定义 \(a_i \ge b_i\) 的比赛为下降类,\(a_i \lt b_i\) 的比赛为上升类。
如果只有下降类,显然可以全部都打,按 \(a_i\) 或者 \(b_i\) 排序都可以。
如果只有上升类,这就相当于有若干区间 \([a_i,b_i]\),求使得这些区间不交的最大区间数量。这是一个经典的贪心问题,按 \(b_i\) 排序,每次选不覆盖的即可。
两者都有的情况,先视作只有上升类的情况,填入上升类。
记某个上升类为 \([a_1,b_1]\),另一个下降类为 \([b_2,a_2]\)。分析可知,只有当 \(a_1 \lt b_2 \le a_2 \lt b_1\),也就是下降类被上升类完全覆盖时,二者只能选一,此时选 \(b\) 更小的下降类一定更优。其他情况都可以同时选到二者。
可以通过排序策略归一上述讨论。只有上升类的情况,需要按 \(b_i\) 升序排序,也就是按 \(\max(a_i,b_i)\) 升序排序;下降类被上升类完全覆盖时,也可以按 \(\max(a_i,b_i)\) 升序排序,此时还有一个边界情况:右边界相等时二者是可以同时取到的,需要先选左边界较小的,因此第二关键字按 \(\min(a_i,b_i)\) 升序排序。
排序后模拟即可。
时间复杂度 \(\mathcal{O}(n\log n)\)。
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void solve(){
int n;
cin >> n;
vector<ll> a(n+1),b(n+1);
for (int i=1;i<=n;i++){
cin >> a[i] >> b[i];
}
vector<int> ord(n+1);
iota(ord.begin(),ord.end(),0);
sort(ord.begin()+1,ord.end(),[&](int i,int j){
if (max(a[i],b[i])!=max(a[j],b[j])){
return max(a[i],b[i])<max(a[j],b[j]);
}
return min(a[i],b[i])<min(a[j],b[j]);
});
ll x = 0;
int res = 0;
for (int i=1;i<=n;i++){
if (x<=a[ord[i]]){
res++;
x = max(x,b[ord[i]]);
}
}
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号