算法导论-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 
#include 
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"<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"< heap_size)
	{
		cout<<"there's no node i"< A[i])
		Heap_Increase_Key(A, i, key);
	else//否则,向下调整
	{
		A[i] = key;
		Max_Heapify(A, i);
	}
}
2、贪心算法:

//main.cpp
#include 
#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<>A[i].start>>A[i].finish;
	}
	//贪心算法
	Greedy();
	return 0;
}

转自: http://blog.csdn.net/mishifangxiangdefeng/article/details/7747779。


你可能感兴趣的:(数据结构和算法,算法导论)