AOE算法典例 —— 拍摄电影最短时间问题

一、问题描述:

要完成一部电影需要很多环节,如下:
1、 项目启动到确定导演需要 1 个时间,已确定导演到完善细节需要 2 个时间,已经完善细节到开始拍摄需要 2 个时间。
2、 项目启动到确定演员需要 3 个时间,已确定导演到已确定演员需要 1 个时间,已确定演员到开始拍摄需要 2 个时间。
3、 项目启动到完成场地确认需要 5 个时间,完成演员确定到完成场地确认需要 1 个时间,完成场地确认到完善细节需要 1 个时间,完成场地确认到开始拍摄需要 2 个时间。

可以看出,每个环节的时间不同,且有着严格的先后关系,可以同时做多个工作,如何求出从项目启动到开始拍摄的最短时间呢?下面就是解决此问题的办法。

二、解决方法:

此问题很明显是一个寻找关键路径的问题,我们可以建立一个图的模型,将每一个顶点代表每一个环节,用有向边来表示两顶点之间的活动,边的权值就代表所需要的时间。
由此,我们也看画出关于此问题的带权有向图(AOE图):

在这里插入图片描述

我们能够很容易的找到关键路径为:项目启动->场地确认->完善细节->开始拍摄。即为:
在这里插入图片描述

我们可以计算出最短的时间为8个时间。

虽然对这种整体环节不是很多的情况下,我们能够用肉眼观察到,但是如果所需要的的环节很多,关系也较为复杂的情况时,我们无法较为容易的观察出最短的路径,这时我们自己要设计一个小程序,来解决这种问题,下面就是代码实现。

三、代码实现:

1、代码思路

首先考虑的是对于有向图的表示方法,由于涉及到权值,我们选择用邻接矩阵的方法对有向图的表示,若两顶点之间不连通,则相对应的坐标表示为无限大(代码中表示为一个很大的值),若存在,则对应坐标代表权值。
先将上述的各项环节转换成数字(即为数组下标)这样能更加容易的代码实现:
在这里插入图片描述

由此,我们可以得到上述问题的邻接矩阵:
在这里插入图片描述

再通过关键路径算法来求得关键路径与最短时间,下面是算法分析。

2、主要算法

首先是设置了一个函数来对邻接矩阵进行初始化,并将边权信息输入进去:

void Initialize()
{
	int a,b,c;
	for(i = 0;i < maxs;i++)
		for(j = 0;j < maxs;j++)
			graph[i][j] = maxwt;
	printf("请输入边数:");
	scanf("%d",&m); 
	printf("请输入每条边的顶点与权重:\n");
	for(i = 0;i < m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		graph[a][b] = c;
	}
}

由公式:

在这里插入图片描述

我们可以利用递归算法求得每个节点的最早时间,存入到e[i]数组中:

void Getearly(int a,int last)
{
	if(a == last)
	return;
	for(int i = 0;i < maxs;i++)
	{
		if(graph[a][i] < maxwt)
		{
			if((e[a] + graph[a][i]) > e[i])
			{
				e[i] = e[a] + graph[a][i];
			}
				
				
			Getearly(i,last);
		}
	}
}

同理,也能够得到最迟的时间,存入到l[i]数组中:

void Getlater(int a,int first)
{
	if(a == first)
		return;
	for(int i = 0;i < maxs;i++)
	{
		if(graph[i][a] < maxwt)
		{
			if(l[a] - graph[i][a] < l[i])
			{
				l[i] = l[a] - graph[i][a];
			}
								
			Getlater(i,first);
		}
	}
}

得到以上两个关键数组,就从输入的起始点k = first开始,到k = last结束。如果 l[i] - graph[k][i] == e[k],则k–>i 为关键路径,再将i加入到path[i]数组中。由此得到了path[i] 关于路径的数组,再通过数组代表的每一个顶点,与邻接矩阵所代表的权值为时间可以得到关键路径的总时间。

完整代码:

编译程序:Dev-C++ 编译环境:C++ (因为有些语句在C++里面能够运行,而在C语言里面可能会报错,为了方便选择了C++编译环境)

#include<stdio.h>
#include<stdlib.h>

#define maxwt 1000000 //两点不连通时权值为无限大 
#define maxs 10  //最大边数

 
int graph[maxs][maxs];  //全局变量邻接矩阵来表示有向图
int e[maxs];  //存储各个点最早开始的时间的数组 
int l[maxs];  //存储各个点最晚开始的时间的数组 
int p[maxs];  //记录关键路径

void Initialize();
void Getearly(int a,int last);
void Getlater(int a,int first);
void Getpath();
int i,j,m;
int time = 0;

int main()
{
	Initialize();  //初始化邻接矩阵
	Getpath();
}
void Initialize()
{
	int a,b,c;
	for(i = 0;i < maxs;i++)
		for(j = 0;j < maxs;j++)
			graph[i][j] = maxwt;
	printf("请输入边数:");
	scanf("%d",&m); 
	printf("请输入每条边的顶点与权重:\n");
	for(i = 0;i < m;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		graph[a][b] = c;
	}
}
void Getpath()
{
	int first,last;
	int k,f;
	printf("请输入开始的点和结束的点:\n");
	scanf("%d%d",&first,&last);
	
	for(i = 0;i < maxs;i++)
	{
		l[i] = maxwt;
		e[i] = 0;
		p[i] = maxwt;
		
	 } 
	k = first;
	p[0] = first;
	f = 1;
	Getearly(k,last);
	k = last;
	l[k] = e[k];
	Getlater(k,first);
	
	k = first;

	while(k != last) // 得到关键路径 
	{
		for(i = 0;i < maxs;i++)
		{
			if(graph[k][i] != maxwt && (l[i] - graph[k][i] == e[k]))
			{
				p[f++] = i;
				k = i;
				break;
			}
		}
	}
	printf("关键路径为:\n");
	for(i = 0;p[i] != last;i++)
	{
		printf("%d->",p[i]);
	} /*输出关键路径 */ 
	printf("%d\n",p[i]);
	for(;i > 0;i--)
	{
		time = time + graph[p[i-1]][p[i]]; //path所记录的下标对应矩阵的权值进行相加 
	}
	printf("此方案所花费的时间为:%d",time);
}
void Getearly(int a,int last) //得到每个节点的最早时间,存入到e[i]数组中
{
	if(a == last)
	return;
	for(int i = 0;i < maxs;i++)
	{
		if(graph[a][i] < maxwt)
		{
			if((e[a] + graph[a][i]) > e[i])
			{
				e[i] = e[a] + graph[a][i];
			}
				
				
			Getearly(i,last);
		}
	}
}
void Getlater(int a,int first) //得到每个节点的最晚时间,存入到e[i]数组中
{
	if(a == first)
		return;
	for(int i = 0;i < maxs;i++)
	{
		if(graph[i][a] < maxwt)
		{
			if(l[a] - graph[i][a] < l[i])
			{
				l[i] = l[a] - graph[i][a];
			}
								
			Getlater(i,first);
		}
	}
}
代码初步测试截图:

与初步预测相同。

为了测试代码的正确性,我们把开始的点由0(项目启动)改为1(确定导演)初步预测得到的结果为1(确定导演)->4(确定演员)->5(场地确认)->2(完善细节)->3(开始拍摄)
所用总时间为5个时间。

代码二次测试结果:

在这里插入图片描述

测试正确。

四、算法效率分析:

对各个函数的时间复杂度与空间复杂度分析:

先来看 Initialize()函数(邻接矩阵的初始化)与Getpath()函数(得到关键路径)其主体都是用了两个for循环,其临时占用空间大小主要为一个二维数组,数组的大小跟最大边长有关:很容易此函数的时间复杂度为O(n2),空间复杂度为O(n2)。

Getearly()函数与Getlater()函数很类似,都是用了递归来求得每次调用递归时的时间复杂度为O(n),设调用次数为m次,时间复杂度即为O(mn)由于m与n的值都不确定,我们可以近似的看作为O(n2)。而空间复杂度,由于递归都要存储返回信息,而且每个函数中都有一个二维数组所以空间复杂度也为O(n2).

综上,总结为下图:
在这里插入图片描述

由此可得,此算法的总体时间复杂度为O(n2),而空间复杂度为O(n2).

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_CX_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值