算法导论-16.1-4 活动教室选择问题

来源:互联网 发布:身份证识别网络阅读器 编辑:程序博客网 时间:2024/06/05 04:31

题目:

假设要用很多个教室对一组活动进行调剂。我们希望应用尽可能少的教室来调剂所有的活动。请给出一个有效的贪心算法,来断定哪一个活动应应用哪一个教室。
(这个题目也被成为区间图着色(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)+1int 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);}}
2、贪心算法:

//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 43 50 65 73 85 96 108 118 122 1312 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://blog.csdn.net/mishifangxiangdefeng/article/details/7747779。


0 0
原创粉丝点击