回溯法求解活动安排问题

问题描述

假设有一个需要使用某一资源的n个活动所组成的集合S,S={1,…,n}。该资源任何时刻只能被一个活动所占用,活动i有一个开始时间bi和结束时间ei(bi 一旦某个活动开始执行,中间不能被打断,直到其执行完毕。若活动i和活动j有bi≥ej或bj≥ei,则称这两个活动兼容。
设计算法求一种最优活动安排方案,使得所有安排的活动个数最多。

问题求解

采用回溯法求解,相当于找到S={1,…,n}的某个排列即调度方案,使得其中所有兼容活动个数最多,显然对应的解空间是一个是排列树。
直接采用排列树递归框架实现,对于每一种调度方案求出所有兼容活动个数,通过比较求出最多活动个数maxsum,对应的调度方案就是最优调度方案bestx,即为本问题的解。

对于一种调度方案,如何计算所有兼容活动的个数呢?因为其中可能存在不兼容的活动。
例如,下表的4个活动,若调度方案为(1,2,3,4),求所有兼容活动个数的过程如下:
回溯法求解活动安排问题_第1张图片
置当前活动的结束时间laste=0,所有兼容活动个数sum=0。
活动1:其开始时间为1,大于等于laste,属于兼容活动,选取它,sum增加1,sum=1,置laste=其结束时间=3。
活动2:其开始时间为2,小于laste,属于非兼容活动,不选取它。
活动3:其开始时间为4,大于等于laste,属于兼容活动,选取它,sum增加1,sum=2,置laste=其结束时间=8。
活动4:其开始时间为6,小于laste,属于非兼容活动,不选取它。
该调度方案的所有兼容活动个数sum为2。

产生所有排列,每个排列x=(x[1],x[2],…,x[n])对应一种调度方案
计算每种调度方案的兼容活动个数sum
比较求出最大的兼容活动个数maxsum和最优方案bestx

代码

struct Action
{
     
	int b;//开始时间
	int e;//结束时间
};
int n = 4;
Action A[] = {
      {
     0,0},{
     1,3},{
     2,5},{
     4,8},{
     6,10} };

int x[MAXN];//临时解向量
int bestx[MAXN];//最优解向量
int laste = 0;//结束时间
int sum = 0;
int maxsum = 0;

void dfs(int i)
{
     
	if (i > n)
	{
     
		if (sum > maxsum)
		{
     
			maxsum = sum;
			for (int j = 1; j <= n; j++)
				bestx[j] = x[j];
		}
	}
	else
	{
     
		for (int j = i; j <= n; j++)
		{
     
			swap(x[i], x[j]);
			int sum1 = sum;
			int laste1 = laste;
			if (A[x[j]].b >= laste)//开始时间大于当前结束时间
			{
     
				sum++;
				laste = A[x[j]].e;
			}
			dfs(i + 1);
			//回溯
			swap(x[i], x[j]);
			sum = sum1;
			laste = laste1;
		}
	}
}

void dispasolution()
{
     
	int laste = 0;
	for (int j = 1; j <= n; j++)
	{
     
		if (A[bestx[j]].b >= laste)
		{
     
			cout << "选取活动" << bestx[j] << " ";
			laste = A[bestx[j]].e;//更改为当前活动的结束时间
		}
	}
}

算法分析

该算法对应解空间树是一棵排列树,与求全排列算法的时间复杂度相同,即为O(n!)。

你可能感兴趣的:(算法,算法,dfs,c++)