演算法導論-16.1-4 活動教室選擇問題

sushauai發表於2015-12-22

演算法導論-16.1-4 活動教室選擇問題

 u012243115  2014-11-14  92 檢視  0評論 原文

題目:

假設要用很多個教室對一組活動進行調劑。我們希望應用盡可能少的教室來調劑所有的活動。請給出一個有效的貪心演算法,來斷定哪一個活動應應用哪一個教室。

(這個題目也被成為區間圖著色(interval-graph coloring)題目。我們可作出一個區間圖,其頂點為已知的活動,其邊連線著不相容的活動。為使任兩個相鄰結點的色彩均不雷同,所需的起碼色彩對應於找出調劑給定的所有活動所需的起碼教室數。)

思考:

常規演算法:

針對所有活動,先用16.1中的方法選擇一個教室安排活動,然後對剩餘的活動再選擇一個新的教室,依次這樣安排,直到活動全部安排完。

這個演算法的時間複雜度是O(n^2),稍換一個角度,就能得到一個O(nlgn)的演算法

O(nlgn)演算法:
針對一個特定的活動,為它選擇一個適合的教室。對所有已經選擇過的教室,用一個最大堆的優先佇列(儲存活動的開始時間)來維護,比較的依據是在這個教室中最早開始的活動的開始時間。

具體步驟是這樣的:
step1:對所有活動按結束時間從小到大排序。

step2:從最後一個活動開始,向第一個活動,依次針對每個活動做以下處理

(1)獲取堆頂元素的資訊

(2)如果堆頂的活動開始時間早於當前活動的結束時間,則申請一個新的教室,把活動的開始時間填入堆中

(3)如果堆頂的活動開始時間晚於當前活動的結束時間,刪除堆頂,並就把當前活動填入堆中。//此時即是每個教室活動的最優選擇

(4)選擇下一個活動,直到所有活動都處理過

程式碼:O(lgn)

1、堆的相關操作:

  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. }  
2、貪心演算法:

  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. }  


轉自:http://www.lai18.com/content/632357.html

相關文章