演算法導論-16.1-4 活動教室選擇問題
題目:
假設要用很多個教室對一組活動進行調劑。我們希望應用盡可能少的教室來調劑所有的活動。請給出一個有效的貪心演算法,來斷定哪一個活動應應用哪一個教室。(這個題目也被成為區間圖著色(interval-graph coloring)題目。我們可作出一個區間圖,其頂點為已知的活動,其邊連線著不相容的活動。為使任兩個相鄰結點的色彩均不雷同,所需的起碼色彩對應於找出調劑給定的所有活動所需的起碼教室數。)
思考:
常規演算法:針對所有活動,先用16.1中的方法選擇一個教室安排活動,然後對剩餘的活動再選擇一個新的教室,依次這樣安排,直到活動全部安排完。
這個演算法的時間複雜度是O(n^2),稍換一個角度,就能得到一個O(nlgn)的演算法
O(nlgn)演算法:
針對一個特定的活動,為它選擇一個適合的教室。對所有已經選擇過的教室,用一個最大堆的優先佇列(儲存活動的開始時間)來維護,比較的依據是在這個教室中最早開始的活動的開始時間。
具體步驟是這樣的:
step1:對所有活動按結束時間從小到大排序。
step2:從最後一個活動開始,向第一個活動,依次針對每個活動做以下處理
(1)獲取堆頂元素的資訊
(2)如果堆頂的活動開始時間早於當前活動的結束時間,則申請一個新的教室,把活動的開始時間填入堆中
(3)如果堆頂的活動開始時間晚於當前活動的結束時間,刪除堆頂,並就把當前活動填入堆中。//此時即是每個教室活動的最優選擇
(4)選擇下一個活動,直到所有活動都處理過
程式碼:O(lgn)
1、堆的相關操作:
- //Heap.h
- #include <iostream>
- #include <stdio.h>
- using namespace std;
- #define PARENT(i) (i)>>1
- #define LEFT(i) (i)<<1
- #define RIGHT(i) ((i)<<1)+1
- int length = 0;//陣列中元素的個數
- int heap_size = 0;//屬於堆的元素個數,看到HeapSort就會明白
- /*************************以下是堆處理函式****************************************/
- //使以i結點為根結點的子樹成為堆,呼叫條件是確定i的左右子樹已經是堆,時間是O(lgn)
- //遞迴方法
- void Max_Heapify(int *A, int i)
- {
- int l = LEFT(i), r = RIGHT(i), largest;
- //選擇i、i的左、i的右三個結點中值最大的結點
- if(l <= heap_size && A[l] > A[i])
- largest = l;
- else largest = i;
- if(r <= heap_size && A[r] > A[largest])
- largest = r;
- //如果根最大,已經滿足堆的條件,函式停止
- //否則
- if(largest != i)
- {
- //根與值最大的結點互動
- swap(A[i], A[largest]);
- //交換可能破壞子樹的堆,重新調整子樹
- Max_Heapify(A, largest);
- }
- }
- /**********************以下是優先佇列處理函式****************************************/
- //將元素i的關鍵字增加到key,要求key>=A[i]
- void Heap_Increase_Key(int *A, int i, int key)
- {
- if(key < A[i])
- {
- cout<<"new key is smaller than current key"<<endl;
- exit(0);
- }
- A[i] = key;
- //跟父比較,若A[PARENT(i)]<A[i],則交換
- //若執行到某個結點時A[PARENT(i)]>A[i],就跳出迴圈
- while(A[PARENT(i)] > 0 && A[PARENT(i)] < A[i])
- {
- swap(A[PARENT(i)], A[i]);
- i = PARENT(i);
- }
- }
- //把key插入到集合A中
- void Max_Heap_Insert(int *A, int key)
- {
- if(heap_size == 99)
- {
- cout<<"heap is full"<<endl;
- exit(0);
- }
- heap_size++;length++;
- A[heap_size] = -0x7fffffff;
- Heap_Increase_Key(A, heap_size, key);
- }
- //返回A中最大關鍵字,時間O(1)
- int Heap_Maximum(int *A)
- {
- return A[1];
- }
- //去掉並返回A中最大關鍵字,時間O(lgn)
- int Heap_Extract_Max(int *A)
- {
- if(heap_size < 1)
- {
- cout<<"heap underflow"<<endl;
- exit(0);
- }
- //取出最大值
- int max = A[1];
- //將最後一個元素補到最大值的位置
- A[1] = A[heap_size];
- heap_size--;
- //重新調整根結點,維護堆的性質
- Max_Heapify(A, 1);
- //返回最大值
- return max;
- }
- //刪除堆中第i個元素
- void Heap_Delete(int *A, int i)
- {
- if(i > heap_size)
- {
- cout<<"there's no node i"<<endl;
- exit(0);
- }
- //把最後一個元素補到第i個元素的位置
- int key = A[heap_size];
- heap_size--;
- //如果新值比原A[i]大,則向上調整
- if(key > A[i])
- Heap_Increase_Key(A, i, key);
- else//否則,向下調整
- {
- A[i] = key;
- Max_Heapify(A, i);
- }
- }
- //main.cpp
- #include <iostream>
- #include "Heap.h"
- using namespace std;
- #define N 11
- //用於儲存每個活動的資訊
- struct node
- {
- int id;//記錄它是第幾個活動
- int start;//開始時間
- int finish;//結束時間
- }A[N+1];
- //用於排序
- bool cmp(node a, node b)
- {
- return a.finish < b.finish;
- }
- //最大堆
- int H[N+1];
- //O(lgn)貪心演算法
- void Greedy()
- {
- //對所有活動的結束時間從小到大排序
- sort(A+1, A+N+1, cmp);
- int i, ret = 0;
- //從最後一個活動開始,到第一個活動,依次針對每個活動做以下處理
- for(i = N; i >= 1; i--)
- {
- //1)獲取堆頂元素的資訊(4)更新堆(5)選擇下一個活動,直到所有活動都處理過
- int temp = Heap_Maximum(H);
- //(2)如果堆頂的活動開始時間早於當前活動的結束時間,則:
- if(temp < A[i].finish)
- {
- //申請一個新的教室
- ret++;
- //把活動的開始時間填入其中
- Max_Heap_Insert(H, A[i].start);
- }
- //(3)如果堆頂的活動開始時間晚於當前活動的結束時間,則:
- else
- {
- //刪除堆頂,並插入新元素
- Heap_Extract_Max(H);
- Max_Heap_Insert(H, A[i].start);
- }
- //選擇下一個活動,直到所有活動都處理過
- }
- cout<<ret<<endl;
- }
- /*
- 1 4
- 3 5
- 0 6
- 5 7
- 3 8
- 5 9
- 6 10
- 8 11
- 8 12
- 2 13
- 12 14
- */
- int main()
- {
- int i;
- //輸入測試資料
- for(i = 1; i <= N; i++)
- {
- A[i].id = i;
- cin>>A[i].start>>A[i].finish;
- }
- //貪心演算法
- Greedy();
- return 0;
- }
轉自:http://www.lai18.com/content/632357.html。
相關文章
- 演算法導論16.1 活動選擇問題演算法
- 活動選擇問題理解貪心演算法演算法
- 物聯網工程導論第二版答案選擇題
- 為你的迴歸問題選擇最佳機器學習演算法機器學習演算法
- 演算法學習之路|選擇題演算法
- 選擇問題——選取第K小元素
- 這就是選擇排序的問題排序
- CSS選擇器常見問題CSS
- [課程複習] 軟體工程導論之經典題目回顧 (一)選擇題、填空題1軟體工程
- 前端面試題:演算法-選擇排序前端面試題演算法排序
- 機器學習處理問題如何選擇一個合適的演算法?機器學習演算法
- 《演算法導論》演算法
- 【爬坑日記】.class.class選擇器的選擇問題
- 演算法導論第二章思考題演算法
- ListView Item 選擇問題解決之道View
- jQuery 選擇器彙總-思維導圖-選擇器jQuery
- 選擇問題(求第k個最小元素)
- MySQL 你可能忽視的選擇問題MySql
- EditText選擇模式的一些問題模式
- 特徵選擇和特徵生成問題初探特徵
- 演算法導論-堆排序演算法排序
- 演算法導論-快速排序演算法排序
- 演算法導論學習之三:排序之C語言實現:選擇排序,插入排序,歸併排序演算法排序C語言
- 選擇物聯網路卡需注意哪些問題
- 選擇伺服器需要關注哪些問題伺服器
- 排序演算法__選擇排序排序演算法
- 常用演算法-選擇排序演算法排序
- 排序演算法:選擇排序排序演算法
- java選擇排序演算法Java排序演算法
- Oracle建立索引選擇合適的可選項及效率問題Oracle索引
- 修改題庫(選擇題) (轉)
- 演算法導論-第6章演算法
- 演算法導論FFT程式碼演算法FFT
- 2 Elment Ui 日期選擇器 格式化問題UI
- 選擇直播美顏工具時應注意哪些問題?
- 選擇資料分析工具時要注意哪些問題
- 卷積核大小選擇、網路層數問題卷積
- 淺談5G頻段的選擇問題