贪心-会议安排
Leetcode 630. 课程表 III
前面两题是拓扑排序,和这题没有实际联系
看起来像 n 个区间,顺序执行,怎么有点像会议安排,结论:贪心,按结束时间排序
具体的,每次从剩下未安排会议中选出最早结束且与已安排会议不冲突的会议;
写了一下:
int scheduleCourse(vector<vector<int>>& courses) {
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq; // 每次从剩下未安排会议中选出最早结束且与已安排会议不冲突的会议;
for(auto &course : courses) {
pq.push(make_pair(course[1], course[0]));
}
int time = 0, cnt = 0;
while(!pq.empty()) {
pair<int, int> cur = pq.top();
pq.pop();
cout << time << " " << cur.first << " " << cur.second << endl;
if(time + cur.second <= cur.first) {
time += cur.second;
cnt++;
}
}
return cnt;
}
测评通过,87/91,[[0,5], [4,6], [2,6]]
,预期是2,我的结果只有1
先按结束时间排序,再依次枚举,
等同于枚举右边界,表示这个时间内可以开的会议数,如果可以开就直接开,如果不能则替换出一个最长的(当最长的大于当前值时)
下面的实现里,此时的优先队列不是剩下会议的,而是已经放入的会议
把每次push和pop的打印出来,就看的很清楚了
class Solution {
public:
// [[0,5], [4,6], [2,6]]
// 将排好的放到优先队列中,当遇到不能直接加的时候,将队列中的最长的课程替换
int scheduleCourse(vector<vector<int>>& courses) {
vector<pair<int, int>> courses_time;
for(auto &course : courses) {
courses_time.push_back(make_pair(course[1], course[0]));
}
sort(courses_time.begin(), courses_time.end());
// 默认顺序是从大到小
priority_queue<int> pq; // 每次从剩下未安排会议中选出最早结束且与已安排会议不冲突的会议;
int time = 0; // 真实使用的时间
for(auto &course : courses_time) {
if(time + course.second <= course.first) {
time += course.second;
pq.push(course.second);
cout << "push: " << course.first << " " << course.second << endl;
} else {
if(!pq.empty()) {
int duration = pq.top();
if(duration > course.second) { // 持续时间
time += course.second;
time -= duration;
cout << "pop: " << pq.top().first << " " << pq.top().second << endl;
pq.pop();
pq.push(course.second);
cout << "push: " << course.first << " " << course.second << endl;
}
}
}
}
return pq.size();
}
};
Leetcode 1705. 吃苹果的最大数目
题意:有n天,每天会产生一些苹果,且都有过期时间,每天最多只能吃一个苹果,求最多能吃几个苹果
方法:按天枚举,每次选过期时间最小的吃
class Solution {
public:
int eatenApples(vector<int>& apples, vector<int>& days) {
priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>>pq;
int n = apples.size(), ans = 0, i = 0;
while(true) { // 枚举苹果,其实也是枚举天数,但在这里两者相等
if(i < n && apples[i] > 0) pq.push(make_pair(i+days[i], apples[i]));
while((!pq.empty()) && pq.top().first <= i) pq.pop(); // 把到期的都去掉
if(!pq.empty()) {
auto p = pq.top();pq.pop();
ans++;
if(p.second > 1) pq.push(make_pair(p.first, p.second-1));
} else {
if(i >= n) break; // 不会再有苹果且队列为空
}
i++;
}
return ans;
}
};
一个很类似的问题是
Leetcode 1353. 最多可以参加的会议数目
明明更难一点却是一个中等题hhh
题意:有n个会议,每个会议最早开始时间si和最晚结束时间ei,可以从中选一天参加,求最多参加多少天会议
方法:其实和上面那个问题等价,相当于si有一个苹果,过期时间是ei,可以在中间某一个吃掉,问最多能吃几个苹果
所以方法一样,先将会议们按起始时间排序,枚举每一天,每次选结束时间最小的开会
class Solution {
public:
int maxEvents(vector<vector<int>>& events) {
sort(events.begin(), events.end(), [](vector<int>& a, vector<int>& b) {
if(a[0] == b[0]) return a[1] < b[1];
return a[0] < b[0];
}); // 先按开始时间排序
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>>pq;
int ans = 0;
int max_day = -1;
for(auto event : events) {
if(event[1] > max_day) max_day = event[1];
}
int i = 0, n = events.size();
for(int day = 1;day <= max_day;day++) { // 按天枚举
while(i < n && events[i][0] == day) { // 将这天开始的压入队列
pq.push(make_pair(events[i][1], events[i][0]));
i++;
}
while((!pq.empty()) && pq.top().first < day) pq.pop(); // 去除到已到期的
if(!pq.empty()) { // 选最小的开(可能暂时没有会议)
pq.pop();
ans++;
}
}
return ans;
}
};
这一题的进阶版,
LCP 32. 批量处理任务
待完成
个性签名:时间会解决一切