第六届辽宁省大学生程序设计竞赛 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;
}
posted @ 2026-05-21 20:36  kzssCCC  阅读(6)  评论(0)    收藏  举报