算法导论-16.1-3

题目:

假设要用很多个教室对一组活动进行调剂。我们希望应用尽可能少的教室来调剂所有的活动。请给出一个有效的贪心算法,来断定哪一个活动应应用哪一个教室。
(这个题目也被成为区间图着色(interval-graph coloring)题目。我们可作出一个区间图,其顶点为已知的活动,其边连接着不兼容的活动。为使任两个相邻结点的色彩均不雷同,所需的起码色彩对应于找出调剂给定的所有活动所需的起码教室数。)

思考:

常规算法:

针对一个特定的教室,然后做类似于16.1中的工作,从剩余活动中尽量多地安排活动到这个教室。如果活动安排完了,就不需要更多的教室了。如果活动没有安排完,再选择一个新的教室,做同样的工作,直到所有的活动都安排完。

这个算法的时间复杂度是O(n^2),稍换一个角度,就能得到一个O(nlgn)的算

O(lgn)算法:

针对一个特定的活动,为它选择一个适合的教室。对所有已经选择过的教室,用一个最大堆来维护,比较的依据是在这个教室中最早开始的活动的开始时间

具体步骤是这样的:

step1:对所有活动的结束时间从小到大排序

step2:从最后一个活动开始,向第一个活动,依次针对每个活动做以下处理(1)获取堆顶元素的信息(2)如果堆顶的活动开始时间早于当前活动的结束时间,则申请一个新的教室,把活动的开始时间填入其中,再把教室作为一个结点存入堆中(3)如果堆顶的活动开始时间晚于当前活动的结束时间,就把当前活动填入堆顶元素中(4)选择下一个活动,直到所有活动都处理过

代码:O(lgn)

[cpp]  view plain copy
  1. //Heap.h  
  2. #include <iostream>  
  3. #include <stdio.h>  
  4. using namespace std;  
  5.   
  6. #define PARENT(i) (i)>>1  
  7. #define LEFT(i) (i)<<1  
  8. #define RIGHT(i) ((i)<<1)+1  
  9.   
  10. int length = 0;//数组中元素的个数  
  11. int heap_size = 0;//属于堆的元素个数,看到HeapSort就会明白  
  12.   
  13. /*************************以下是堆处理函数****************************************/  
  14. //使以i结点为根结点的子树成为堆,调用条件是确定i的左右子树已经是堆,时间是O(lgn)  
  15. //递归方法  
  16. void Max_Heapify(int *A, int i)  
  17. {  
  18.     int l = LEFT(i), r = RIGHT(i), largest;  
  19.     //选择i、i的左、i的右三个结点中值最大的结点  
  20.     if(l <= heap_size && A[l] > A[i])  
  21.         largest = l;  
  22.     else largest = i;  
  23.     if(r <= heap_size && A[r] > A[largest])  
  24.         largest = r;  
  25.     //如果根最大,已经满足堆的条件,函数停止  
  26.     //否则  
  27.     if(largest != i)  
  28.     {  
  29.         //根与值最大的结点交互  
  30.         swap(A[i], A[largest]);  
  31.         //交换可能破坏子树的堆,重新调整子树  
  32.         Max_Heapify(A, largest);  
  33.     }  
  34. }  
  35. /**********************以下是优先队列处理函数****************************************/  
  36. //将元素i的关键字增加到key,要求key>=A[i]  
  37. void Heap_Increase_Key(int *A, int i, int key)  
  38. {  
  39.     if(key < A[i])  
  40.     {  
  41.         cout<<"new key is smaller than current key"<<endl;  
  42.         exit(0);  
  43.     }  
  44.     A[i] = key;  
  45.     //跟父比较,若A[PARENT(i)]<A[i],则交换  
  46.     //若运行到某个结点时A[PARENT(i)]>A[i],就跳出循环  
  47.     while(A[PARENT(i)] > 0 && A[PARENT(i)] < A[i])  
  48.     {  
  49.         swap(A[PARENT(i)], A[i]);  
  50.         i = PARENT(i);  
  51.     }  
  52. }  
  53. //把key插入到集合A中  
  54. void Max_Heap_Insert(int *A, int key)  
  55. {  
  56.     if(heap_size == 99)  
  57.     {  
  58.         cout<<"heap is full"<<endl;  
  59.         exit(0);  
  60.     }  
  61.     heap_size++;length++;  
  62.     A[heap_size] = -0x7fffffff;  
  63.     Heap_Increase_Key(A, heap_size, key);  
  64. }  
  65. //返回A中最大关键字,时间O(1)  
  66. int Heap_Maximum(int *A)  
  67. {  
  68.     return A[1];  
  69. }  
  70. //去掉并返回A中最大关键字,时间O(lgn)  
  71. int Heap_Extract_Max(int *A)  
  72. {  
  73.     if(heap_size < 1)  
  74.     {  
  75.         cout<<"heap underflow"<<endl;  
  76.         exit(0);  
  77.     }  
  78.     //取出最大值  
  79.     int max = A[1];  
  80.     //将最后一个元素补到最大值的位置  
  81.     A[1] = A[heap_size];  
  82.     heap_size--;  
  83.     //重新调整根结点  
  84.     Max_Heapify(A, 1);  
  85.     //返回最大值  
  86.     return max;  
  87. }  
  88. //删除堆中第i个元素  
  89. void Heap_Delete(int *A, int i)  
  90. {  
  91.     if(i > heap_size)  
  92.     {  
  93.         cout<<"there's no node i"<<endl;  
  94.         exit(0);  
  95.     }  
  96.     //把最后一个元素补到第i个元素的位置  
  97.     int key = A[heap_size];  
  98.     heap_size--;  
  99.     //如果新值比原A[i]大,则向上调整  
  100.     if(key > A[i])  
  101.         Heap_Increase_Key(A, i, key);  
  102.     else//否则,向下调整  
  103.     {  
  104.         A[i] = key;  
  105.         Max_Heapify(A, i);  
  106.     }  
  107. }  


 

[cpp]  view plain copy
  1. //main.cpp  
  2. #include <iostream>  
  3. #include "Heap.h"  
  4. using namespace std;  
  5.   
  6. #define N 11  
  7. //用于存储每个活动的信息  
  8. struct node  
  9. {  
  10.     int id;//记录它是第几个活动  
  11.     int start;//开始时间  
  12.     int finish;//结束时间  
  13. }A[N+1];  
  14. //用于排序  
  15. bool cmp(node a, node b)  
  16. {  
  17.     return a.finish < b.finish;  
  18. }  
  19. //最大堆  
  20. int H[N+1];  
  21. //O(lgn)贪心算法  
  22. void Greedy()  
  23. {  
  24.     //对所有活动的结束时间从小到大排序  
  25.     sort(A+1, A+N+1, cmp);  
  26.     int i, ret = 0;  
  27.     //从最后一个活动开始,向第一个活动,依次针对每个活动做以下处理  
  28.     for(i = N; i >= 1; i--)  
  29.     {  
  30.         //1)获取堆顶元素的信息(4)更新堆(5)选择下一个活动,直到所有活动都处理过  
  31.         int temp = Heap_Maximum(H);  
  32.         //(2)如果堆顶的活动开始时间早于当前活动的结束时间,则:  
  33.         if(temp < A[i].finish)  
  34.         {  
  35.             //申请一个新的教室  
  36.             ret++;  
  37.             //把活动的开始时间填入其中,再把教室作为一个结点存入堆中  
  38.             Max_Heap_Insert(H, A[i].start);  
  39.         }  
  40.         //(3)如果堆顶的活动开始时间晚于当前活动的结束时间,则:  
  41.         else  
  42.         {  
  43.             //就把当前活动填入堆顶元素中  
  44.             Heap_Extract_Max(H);  
  45.             Max_Heap_Insert(H, A[i].start);  
  46.         }  
  47.         //选择下一个活动,直到所有活动都处理过  
  48.     }  
  49.     cout<<ret<<endl;  
  50. }  
  51. /* 
  52. 1 4 
  53. 3 5 
  54. 0 6 
  55. 5 7 
  56. 3 8 
  57. 5 9 
  58. 6 10 
  59. 8 11 
  60. 8 12 
  61. 2 13 
  62. 12 14 
  63. */  
  64. int main()  
  65. {  
  66.     int i;  
  67.     //输入测试数据  
  68.     for(i = 1; i <= N; i++)  
  69.     {  
  70.         A[i].id = i;  
  71.         cin>>A[i].start>>A[i].finish;  
  72.     }  
  73.     //贪心算法  
  74.     Greedy();  
  75.     return 0;  
  76. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值