第九讲--最短路、拓扑排序、关键路径
复习
1.n个结点的无向图,如果他是连通的,最少有多少条边?最多有多少条边?
2.图有哪几种存储结构?
3.图的遍历方式(把上节课的例子再练习一下,无向图和有向图的遍历方式都得会)。
4.如何判断一个图是连通的?如何判断一个图里有多少个强连通子图?
6.无向图中删除某个顶点的复杂度是多少(用邻接表表示)?
5.最小生成树很重要!两种算法,算法之间的区别是什么?什么时候用哪种算法?具体的实现方
式是什么?
一、最短路(必考大题)
最短路主要求的是从一个特定的结点开始,分别到其他结点的最短路径是多少。
比如我们要求北京分别到其他城市的最短路径,我们最后应该求出一个一维数组dist[6]。这里我们对城市编号北京1,西安2,青岛3,武汉4,拉萨5,福州6。那么dist[5]表示北京到拉萨的最短距离,其他的都一样。
1.初始化,首先把北京到其他城市的距离进行初始化,也就是 dist[1] = 0,dist[2] = 6,dist[3] = 5,dist[4] = 1,dist[5] = 正无穷,dist[6] = 正无穷。然后把北京标记为1,代码中是flag[1] = 1(0代表还没更新),代表北京已经被更新了,并且是最短的。
for(int i = 1;i<=n;i++)//初始化
{
//1表示北京 n表示一共有多少个城市
dis[i] = a[1][i]; //a是邻接矩阵
}
2.选取距离最近的城市,并且这个城市是没有被更新的。
minn = 9999999;
for(int j = 1;j<=n;j++) //找到一个最小的
{
if(dis[j]<minn&&flag[j]==0) //如果他是最小的,并且他还没有加入到北京的怀抱中
{
minn = dis[j]; //最小值替换
min_r = j; //最小值对应的城市替换
}
}
3.找到这个城市以后,更新dist,然后把这个城市标记为已更新。
flag[min_r] = 1;
for(int j = 1;j<=n;j++)
{
if(flag[j]==0&&dis[j]>(dis[min_r]+a[min_r][j]))
{
dis[j] = dis[min_r]+a[min_r][j];
}
}
然后1,2,3我们一共需要做n-1步,整体的代码是:
void Dijkstra()
{
int sum = 0;
int min_r,minn;
for(int i = 1;i<=n;i++)//初始化
{
dis[i] = a[1][i]; //a是邻接矩阵
}
flag[1] = 1;
/*每次找最小距离的那个点 然后遍历那个点看有没有其他点更小 重复N-1次*/
for(int i = 1;i<n;i++)
{
minn = 9999999;
for(int j = 1;j<=n;j++) //找到一个最小的
{
if(dis[j]<minn&&flag[j]==0)
{
minn = dis[j];
min_r = j;
}
}
flag[min_r] = 1;
for(int j = 1;j<=n;j++)
{
if(flag[j]==0&&dis[j]>(dis[min_r]+a[min_r][j]))
{
dis[j] = dis[min_r]+a[min_r][j];
}
}
}
}
举个栗子
我们每一步操作转化为表格形式:
可以看到,一共有5个点,我们需要遍历4趟,每一趟确定一个最短路(图中标红)。最下面一行是当前确定的结点。这个图得会画,并且得知道到某个结点的最短路分别由那些路组成。
二、Prim,Kruskal,Dijkstra三种算法的对比
1.prim和kurskal两个算出来的最小生成树一样吗?
2.为什么会造成最小生成树不一样?
3.最小生成树的代价一样吗?
4.是否所有权值最小的边都会出现在最小生成树中?
二、拓扑排序(考纲有,但是好像不考)
举个栗子
唐僧师徒五人要从大唐到达天竺取经,其中,其有好多条路供他们选择,但是每到达一个地方,必须有通过这个地方所需的通关文牒,比如通过拉萨,必须要有成都和深圳(连像拉萨箭头的那两个城市),而通过成都必须要有大唐的通关文牒。这里一条可行的路可以是:大唐->成都->深圳->拉萨->天竺。
1.选取一个不需要通关文牒的城市,比如大唐,获取他的通关文牒,然后把其所连接的成都和深圳的那两条线删除,代表这两个城市所需要的大唐通关文牒已获取。
2.重复步骤1,知道没有结点。
如果我们把成都->深圳的路删除了,会出现什么情况?
拓扑排序要求是不存在环路!
是什么原因造成拓扑排序不唯一?
三、 关键路径
这个东西有点像小学里面的最短时间花费题。比如小明早上起床,煮饭10分钟,洗菜5分钟,刷牙5分钟,吃饭半小时,洗完五分钟,听天气预报5分钟。问小明最短需要花费多长时间,哪些活动影响着小明的最短花费时间?而这些影响最短花费时间的活动就组成了关键路径。
在带权有向图中,以顶点表示事件,有向边表示活动,边上权值表示完成该活动的开销。
这算是我能想到的一个符合现实生活的例子(绞尽脑汁......),问从起床到走人最少需要多长时间?如果想提升总时间,我们应该加快那些步骤?
我们需要知道的是,穿衣的前提是洗完,而洗完必须执行上面的叠被+刷牙 = 5,以及下面的听天气+洗澡 = 6.所以洗完的最早时间应该是6分钟。而叠被和刷牙中有1分钟是可以任意安排的。也就是我们可以在叠被刷牙时候停1分钟也无所谓。而听天气和洗澡是一直要执行不能有间隔的,也就是他们最早发生时间和最晚发生时间是一致的。所以我们需要减少的是听天气和洗澡的时间,这样就会降低洗完所需要的总时间。但是也不能减少太多,比如听天气和洗澡时间减少为4时候,关键路径就改成叠被和刷牙了。
我们要求的就是最早发生时间和最晚发生时间之差为0 的活动组成的路径就是关键路径。
而活动又在边上,边的左面代表活动开始时间,边的右面代表活动的结束时间。所以我们应该先把边两边的结点时间求出来。
先求结点(事件)
1.求AOE网中所有事件的最早发生时间ve()
2.求AOE网中所有事件的最迟发生时间vl()
| V1 | V2 | V3 | V4 | V5 | V6 |
ve(i) | 0 | 3 | 2 | 6 | 6 | 8 |
vl(i) | 0 | 4 | 2 | 6 | 7 | 8 |
再求边(活动)
3.求AOE网中所有活动的最早开始时间e()
4.求AOE网中所有活动的最迟开始时间l()
再算算差值(差值为0 的就是关键活动,关键活动组成关键路径)
5.求AOE网中所有活动的差额d(),找出所有d()=0的活动构成关键路径
| a1 | a2 | a3 | a4 | a5 | a6 | a7 | a8 |
e(i) | 0 | 0 | 3 | 3 | 2 | 2 | 6 | 6 |
l(i) | 1 | 0 | 4 | 4 | 2 | 5 | 6 | 7 |
l-e | 1 | 0 | 1 | 1 | 0 | 3 | 0 | 1 |