贪心算法实例 —— 事情安排

贪心算法实例 —— 事情安排

问题:

本关任务:小张最近很忙,记事本里有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;
}

猜你喜欢

转载自blog.csdn.net/tblwbr520/article/details/109404698