贪心算法实例 —— 事情安排
问题:
本关任务:小张最近很忙,记事本里有n件事情等待处理,每件事处理完毕后,能得到不同的奖励(或报酬),而且都有一个截止日。一件事处理需要一整天时间,并且小张不能同时干其他事情。请你替小张安排一个事情处理的时间表,争取获得最大的奖励(或报酬)。
测试输入:第一行输入整数n,表示n个事情。随后输入n行,每行包括一个事情的截止日(不大于n)和处理收益。
测试输出:输出最大的总收益。
前言:
这是老师布置的一个作业题,我想了很久,没想出来,去网上一搜,找到的代码既没思路又没注释,花了好久看懂,于是写出来分享一下。我只能对这个题目简单的理解,没有对贪心算法进行深层次的分析,因此本博客适合给学算法的小白参考。
思路:
先把每一件事按照报酬的大小排序,依次安排每一件事,安排的规则是:每一件事尽量安排在它的期限所在的那一天,如果这一天被安排了,则往前面安排,如果前面每一天都被安排了,那么这一件事就可以不做,因为按照我们安排的顺序已经安排好的每一件事的报酬都比它多,而且往后面安排也不行,因为期限已过。
疑惑:
为什么要尽量把事情安排到最后期限所在的那一天?
答:按照我们最直接的思维就是先把报酬最多的事情做完,
我们来比较一下两种方法:例:有3件事情,依次是(2,15)、(1,13)、(2,12)。 如果把(2,15)第一天就做了,(1,13)就做不了了,只有(2,12)能做,总报酬是15+12 = 27,如果按照之前的思路,第二天做(2,15), 第一天就能做(1,13),总报酬是15+13 = 28,显然后者正确。
因此,这样安排的目的是尽可能把前面的时间留给那些期限早,但报酬又比较多的事情,何谓比较多?这是相对的,假如上例中的事情(2,12)改为(2,14),再按照我们正确的思路,事情(1,13)就没必要做了,因为(2,14)报酬更多,我们是按报酬大小依次安排的,在安排(2,14)时已经将可以做(1,13)的时间占了,这样是合理的,因为这时候(1,13)的报酬比较少,就算你尽量给它留时间,它也无法 “抢”到时间
代码:
#include <stdio.h>
struct project{
int deadline;//期限
int money;//报酬
}; //将一件事情定义为一个结构体,有期限和报酬两个成员
int main(){
int n;
scanf("%d",&n);
project list[1000];//结构体数组,存储n件事情
int lastDay = 0;
/*lastDay 用来保存所有事件的期限最大值 ,在lastDay这一天
以及之前都有事情可做,往后的天数任务都到期了,想做也做不了
*/
int salary[1000] = {
0};
/*salary[i]存储第 i 天完成任务获得的报酬,salary[i]为零可以表示
第i天没有被安排,因此salary[i]也能表示第i天是否被安排事情
*/
for(int i = 1; i<=n; i++){
scanf("%d %d",&list[i].deadline,&list[i].money);
if(list[i].deadline > lastDay){
lastDay = list[i].deadline;//找出最大的期限
}
}
project temp;//交换两个变量的值时temp做中间变量
//冒泡排序,将每一件事按报酬的大小降序排列
for(int i = 1; i<n; i++){
for(int j = 1; j<n-i; j++){
if(list[j].money<list[j+1].money){
temp = list[j];
list[j] = list[j+1];
list[j+1] = temp;
}
}
}
for(int i = 1; i<=n; i++){
if(salary[list[i].deadline] == 0){
salary[list[i].deadline] = list[i].money;
/*如果第i件事情的最后期限那一天没有被安排,那就把
这件事情安排在这一天,则这一天的报酬就是完成这件事的报酬*/
}
else{
/*如果最后期限那一天被安排了,就只能把这件事往前安排,
,因为过了这一天后事情期限过了。如果往前的每一天都被安排了,这件事情
就可以忽略不做。因为按照我们安排的顺序,已经安排好的每一件事能获得的
报酬都比当前的事情的报酬多*/
for(int j = list[i].deadline - 1; j>=1; j--){
//往前安排
if(salary[j] == 0){
//判断第j天是否被安排
salary[j] = list[i].money;
break;
}
}
}
}
int sum = 0;//sum存储总报酬
for(int i = 1; i<=lastDay; i++){
sum += salary[i];
/*把每一天获得的报酬加起来。根据我们前面的解释,
lastDay之后的每一天没有事情做,所以不会再有报酬了*/
}
printf("%d\n",sum); //输出结果
return 0;
}