【题目描述】
有一次性的工作可用。如果您接受第 -份工作并完成它,您将从您完成工作的那天起获得几天后的奖励。Ni,Bi,Ai
您一天最多可以接受并完成其中一项工作。
但是,您不能重新接受已经完成的工作。
找到您从今天起几天内可以获得的最高总奖励。M
您今天就可以开始工作了。
【输入】
- 输入中的所有值都是整数。
- 1≤N≤105
- 1≤M≤105
- 1≤Ai≤105
- 1≤Bi≤104
【输出】
打印您最迟可以从今天起到第M天内获得的最高总奖励。
解题思路
题目大意是:每天可以选择一个工作并完成,但是工作完成了,并不会马上获得报酬,相当于工作完成后的第x天才收到报酬,所以要选择正确的工作顺序,在有限的天数内获得最大报酬。
这个题目首先思考错了:我用一个结构体存储天数和报酬,然后根据时间从小到大排序,在两个工作时间相同时,按照报酬大的放前面排序。然后将天数从大往小,排工作任务。此时忽略了一个问题,天数小的一直会被优先考虑,所以可能不能达到最大报酬的要求。
换一个计算方式,将结构体根据天数从小到大的顺序排序,然后用一层循环遍历给出的总天数m,其中ans是从零开始递增的结构体下标,里面是一层while循环:如果结构体中得到报酬的天数k[ans].a<=i,就将k[ans].b加入优先队列。
在当前天数i的情况下,加入优先队列的都是可以选择的工作任务,在这个前提下,选择队头,sum = sum + q.top();接着将它出队,继续下面的循环。这样就可以保证在每个天数要求下,选择最大报酬的工作。
代码如下
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
struct node {
int a;//天数
int b;//报酬
}k[100005];
//按照获得报酬的时间长短排序(短的在前面)
//如果时间相同,报酬高的排在前面
bool cmp(struct node&q,struct node &w){
if (q.a == w.a) {
return q.b > w.b;
}
return q.a < w.a;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 0; i < n; i++) {
cin >> k[i].a >> k[i].b;
}
sort(k, k + n, cmp);
//从后往前排工作,把花费时间小的先安排
priority_queue<int> q;//用优先队列(优先队列把报酬按从大到小排列)
int ans = 0;
int sum = 0;
//如果当前任务的天数小于剩余的天数,则表示可以完成它;就把他放到队列里面;(为什么都放里面呢,如果剩i天可以完成这些任务,那么剩余i+1天一定可以完成这些任务,所以只需要挑时间短,报酬高的即可)
for (int i = 1; i <= m; i++) {
//当限制天数为i时,这个工作可以完成,说明在i+1或者更长的天数都可以完成
//此时把报酬保留在优先队列中,只有当天选择并做完的工作移除队列
while (k[ans].a <= i && ans < n) {
q.push(k[ans].b);
ans++;
}
if (q.empty()) {
continue;
}
else {
sum = sum + q.top();
q.pop();
}
}
cout << sum << endl;
}