信息学奥赛一本通 贪心算法
【题目描述】
老师在开学第一天就把所有作业都布置了,每个作业如果在规定的时间内交上来的话才有学分。每个作业的截止日期和学分可能是不同的。例如如果一个作业学分为10,要求在6天内交,那么要想拿到这10学分,就必须在第6天结束前交。
每个作业的完成时间都是只有一天。例如,假设有7次作业的学分和完成时间如下:
作业号 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
期限 | 1 | 1 | 3 | 3 | 2 | 2 | 6 |
学分 | 6 | 7 | 2 | 1 | 4 | 5 | 1 |
最多可以获得15学分,其中一个完成作业的次序为2,6,3,1,7,5,4,注意可能d还有其他方法。
你的任务就是找到一个完成作业的顺序获得最大学分。
【输入】
第一行一个整数N,表示作业的数量。
接下来N行,每行包括两个整数,第一个整数表示作业的完成期限,第二个数表示该作业的学分。
【输出】
输出一个整数表示可以获得的最大学分。保证答案不超过longint范围。
【输入样例】
7 1 6 1 7 3 2 3 1 2 4 2 5 6 1
【输出样例】
15
【分析】
作业是在一个时间段完成(一天),则可以安排在截止日期以及之前的任意一天。优先考虑完成学分高的作业。题目测试数据量比较大,需要考虑节省查找时间,当发现一个截止日期以及它之前的日期都不能安排,则需要记载下来,当下次检查到这个日期时就可以停止了。
//优先安排学分高的作业
for(int i = 0; i < n; i++){
int flag = 0;
//查询截止日期及之前日期,k[j]如果等于1则不用继续往前查了
for(int j = hw[i].d; j >= 1 && k[j] == 0; j--){
//为作业找到可以安排的日期
if(v[j] == 0){
v[j] = 1;
ans += hw[i].c;
flag = 1;
break;
}
}
//当前作业截止日期及它前面的日期都不能安排,则意味着后面查到这个日期就可以停止了
if(flag == 0){
k[hw[i].d] = 1;
}
}
【完整代码】
#include <bits/stdc++.h>
struct Node{
int d,c;
}hw[1000005];
int v[1000005];//标记一个日期状态
int k[1000005];//标记一段日期状态,即截止日期以及它之前的日期状态
bool comp(Node x, Node y){
if(x.c == y.c)
return x.d < y.d;
return x.c > y.c;
}
using namespace std;
int main(int argc, char *argv[]) {
int n;
cin >> n;
for(int i = 0; i < n; i++){
cin >> hw[i].d >> hw[i].c;
}
//按照学分降序
sort(hw,hw+n,comp);
long long ans = 0;
//优先安排学分高的作业
for(int i = 0; i < n; i++){
int flag = 0;
//查询截止日期及之前日期,k[j]如果等于1则不用继续往前查了
for(int j = hw[i].d; j >= 1 && k[j] == 0; j--){
//为作业找到可以安排的日期
if(v[j] == 0){
v[j] = 1;
ans += hw[i].c;
flag = 1;
break;
}
}
//当前作业截止日期及它前面的日期都不能安排,则意味着后面查到这个日期就可以停止了
if(flag == 0){
k[hw[i].d] = 1;
}
}
cout << ans;
return 0;
}