1422:【例题1】活动安排
题目描述
设有 n 个活动的集合 E={1,2,…,n}
,其中每个活动都要求使用同一资源,如演讲会场等,而在同一时间内只有一个活动能使用这一资源。每个活动 i 都有一个要求使用该资源的起始时间 si
和一个结束时间 fi
,且 si < fi
。如果选择了活动 i,则它在半开时间区间 [si, fi)
内占用资源。若区间 [si, fi)
与区间 [sj, fj)
不相交,则称活动 i 与活动 j
是相容的。也就是说,当 si ≥ fj
或 sj ≥ fi
时,活动 i 与活动 j
相容。选择出由相互兼容的活动组成的最大集合。
输入
第 1 行一个整数 n(n ≤ 1000
),接下来 n
行,每行两个整数 si
和 fi
。
输出
输出尽可能多的互相兼容的活动个数。
输入样例
4
1 3
4 6
2 5
1 7
输出样例
2
解决方案
我们可以使用贪心算法来解决这个问题。贪心算法的核心思想是每次选择结束时间最早且与已选择的活动不冲突的活动。具体步骤如下:
- 按活动的结束时间排序:首先将所有活动按结束时间end升序排序。
- 选择活动:从第一个活动开始,依次选择与当前已选择活动集合不冲突且结束时间最早的活动。
贪心原理
为了证明贪心选择性质,我们需要证明在每一步选择当前最早结束的活动是安全的,即这一选择不会影响到最终找到最大兼容活动集合的能力。假设存在一个最优解包含了一个不是当前最早结束时间的活动A,而我们选择了另一个最早结束时间的活动B。如果A和B不相容(即它们的时间区间重叠),那么选择A会导致无法选择B;但是,由于B比A更早结束,选择B之后可以为后续的活动留出更多的时间,因此至少可以达到与选择A相同的兼容活动数量,甚至可能更多。所以,选择最早结束时间的活动是安全且有效的。
简而言之,针对我们的选择活动,其余活动划分成两类,与之有冲突的与没有冲突的。无冲突的不考虑,在与之有冲突的活动中为什么一定选择结束时间最早的,因为这样对后续活动影响最小。
代码实现
以下是完整的代码实现:
#include<bits/stdc++.h>
using namespace std;
// 自定义比较函数,用于按活动的结束时间排序
bool compareBySecond(const pair<int, int>& a, const pair<int, int>& b) {
return a.second < b.second;
}
int main() {
int n;
cin >> n; // 输入活动数量
int num = 0; // 记录最多可以安排的活动数量
vector<pair<int, int>> v1; // 存储活动的开始时间和结束时间
// 输入每个活动的开始时间和结束时间
for(int i = 1; i <= n; i++) {
int start, end;
cin >> start >> end;
v1.push_back({start, end});
}
// 按活动的结束时间排序
sort(v1.begin(), v1.end(), compareBySecond);
// 贪心算法选择活动
while(!v1.empty()) {
int last = v1.begin()->second; // 当前选择的活动的结束时间
v1.erase(v1.begin()); // 删除已选择的活动
num++; // 增加已安排的活动数量
// 删除所有与当前选择的活动冲突的活动
for(auto it = v1.begin(); it != v1.end();) {
if(it->first < last) {
it = v1.erase(it); // 删除冲突的活动
} else {
break; // 由于活动已按结束时间排序,后续活动不会冲突
}
}
}
// 输出最多可以安排的活动数量
cout << num;
return 0;
}