• 活动安排问题之二(贪心)


    活动安排问题之二
     
     
    有若干个活动,第i个开始时间和结束时间是[Si,fi),活动之间不能交叠,要把活动都安排完,至少需要几个教室?
     
    分析:能否按照之一问题的解法,每个教室安排尽可能多的活动,即按结束时间排序,再贪心选择不冲突的活动,安排一个教室之后,剩余的活动再分配一个教室,继续贪心选择……

    反例: A:[1,2)  B:[1,4) C:[5,6) D:[3,7)

    已经按结束时间排好顺序,我们会选择
    教室1: A C
    教室2:  B
    教室3:  D
    需要3个教室。
    但是如果换一种安排方法,我们可以安排AD在一个教室,而BC在另外一个教室,两个教室就够了。

    所以之前的贪心策略解决不了这个问题。
    怎么办?之前的策略是用一个教室找所有它能安排下的活动,即用教室找活动,我们能不能用活动找教室呢?

    策略: 按照开始时间排序优先安排活动,如果冲突,则加一个教室。
    简单地理解一下,策略是这样,我们把活动按照开始时间有小到大的顺序排序。假设目前已经分配了k个教室(显然k初始等于0),对于当前这个活动,
    (1) 如果它能安排在k个教室里的某一个,则把它安排在其中的任何一个教室里,k不变。
    (2) 否则它和每个教室里的活动都冲突,则增加一个教室,安排这个活动。

    这个策略是最优么?

    我们想像一下k增加1的过程: 因为我们是按照开始时间排序的,意味着当前考虑的这个活动开始的时候,k个教室里都有活动没结束(因为如果有一个教室的活动结束了,我们就可以安排这个活动进入那个教室而不冲突,从而不用增加k)。这就意味着在这个活动开始的时间点,算上目前考虑的这个活动,有(k + 1)个活动正在进行,同一时刻有(k + 1)个活动在进行,无论我们如何安排教室,都至少需要(k + 1)个教室。因为每个教室里不能同时进行两个活动。而我们的策略恰好需要(k + 1)个教室,所以是最优的。

    这个策略也告诉我们,如果从时间轴上“宏观”考虑这个问题。考虑每个时间点同时进行的活动个数,作为这个时间点的厚度(把活动开始和结束时间想像成线段,那么每个时间点有多少条线段覆盖它,可以简单理解为“厚度”),我们至少需要最大厚度那么多个教室——因为那时恰好有最大厚度那么多个活动同时进行,而我们这个贪心策略恰好给了我们一个用最大厚度那么多个教室安排全部活动的一个方案。

    如果只需要教室的个数,我们可以把所有开始时间和结束时间排序,遇到开始时间就把厚度加1,遇到结束时间就把厚度减1,显然最初始和最后结束时的厚度是0,在一系列厚度变化的过程中,峰值(最大值)就是最多同时进行的活动数,也是我们至少需要的教室数。
    最后,我们来提供输入输出数据,由你来写一段程序,实现这个算法,只有写出了正确的程序,才能继续后面的课程。
     
    输入

    第一行一个正整数n (n <= 10000)代表活动的个数。
    第二行到第(n + 1)行包含n个开始时间和结束时间。
    开始时间严格小于结束时间,并且时间都是非负整数,小于1000000000
    输出
     
    一行包含一个整数表示最少教室的个数。
     
    输入示例

    3
    1 2
    3 4
    2 9

    输出示例

    2
     
    请选取你熟悉的语言,并在下面的代码框中完成你的程序,注意数据范围,最终结果会造成Int32溢出,这样会输出错误的答案。
    不同语言如何处理输入输出,请查看下面的语言说明。
     
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    long long b[10000+10];
    
    struct nood
    {
    	int start;
    	int end;
    }p[10010];
    
    bool cmp(nood x, nood y)
    {
    	if(x.start == y.start )
    		return x.end >y.end ;
    	else
    	return x.start < y.start ;
    }
    
    int main()
    {
    	int n;
    	int ans=1;
    	long long sum=1;
    	int i,j;
    	scanf("%d",&n);
    	for(int i=0; i<n; i++)
    	{
    		scanf("%d %d",&p[i].start , &p[i].end );
    	}
    	sort(p, p+n, cmp);
    	b[0]=p[0].end ;
    	for(i=1; i<n; i++)
    	{
    		for(j=0; j<sum; j++)
    		{
    			if(b[j] <= p[i].start )//找到房间,更新结束时间
    			{
    				b[j]=p[i].end ;
    				break;
    			}		
    		 } 
    		 if(j==sum)//未找到房间,更新结束时间
    		 {
    		 	b[sum]=p[i].end ;
    		 	++sum;
    		 }
    									
    	}
    	printf("%d
    ",sum);
    	return 0;
     }
     
    贪心问题的第二种题型,用活动来找房间。这种问题是在第一种问题的基础之上,通过增加房间,然后再在每个房间内进行贪心查找
    还有一种代码是和题解思想基本上是一样的,对开始和结束时间分别进行sort排序这种思想需要思考
     
    #include<cstdio>  
    #include<iostream>  
    #include<algorithm>  
    typedef long long LL;  
    using namespace std;  
      
      
        LL startime[10001];  
        LL endtime[10001];  
      
    int main(){  
        int n;  
        cin>>n;  
      
          
        for(int i=0;i<=n-1;i++){  
            cin>>startime[i];  
            cin>>endtime[i];  
        }  
        sort(startime,startime+n);  
        sort(endtime,endtime+n);  
          
        int sum=0;  
        int a=0;int b=0;  
        int memory=0;      
        for(a=0;a<=n-1;a++){  
            if(startime[a]<endtime[b]){  
                sum++;  
                if(sum>memory) memory=sum;  
            }  
            else b++;  
        }  
        cout<<memory;  
        return 0;  
    }   
    

      

     
     
    永远渴望,大智若愚(stay hungry, stay foolish)
  • 相关阅读:
    2.1求二进制数中1的个数
    SQL学习笔记八:SQL Server陷阱
    SQL学习笔记之三:系统表syscommnets
    SQL学习笔记之二:QUOTENAME函数
    SQL学习笔记五:脚本创建角色、用户以及相关操作
    输入表名生成插入、更新存储过程和调用该存储过程的字符串
    没有注意过的count(0),count(1),count(*),count(列名)
    清除开始文档批处理
    SQL学习笔记七:利用脚本附加数据库
    SQL学习笔记六:关于全备/差异/日志备份的恢复
  • 原文地址:https://www.cnblogs.com/h-hkai/p/7448003.html
Copyright © 2020-2023  润新知