AOE网求关键路径(关键活动):
AOE网求解关键路径,所需的是有向无环图(利用拓扑排序,如果序列长度为顶点数,则是无环,小于顶点数则是有环图,有环图是不满足求AOE网的),注意的是,只有一个源点,有一个汇顶点,然后关键路径不一定只有一条。注意,这里要理解:
顶点:事件
边:活动
还有四个数组下面有介绍:
这里我们用到的图:
关键活动已经用红色标记出来
代码如下:
#include <stdio.h>
#include <malloc.h>
#define inf 330
#define max 50
typedef struct node{
int adjvex;
int weight; //权值
struct node *nextarc;
}arcnode;//定义结点的结构体类型
typedef struct {
arcnode *firstarc;
int count;
}vnnode; //定义头结点的结构体
typedef struct {
vnnode adjlist[max];
int n;
int e;
}adjgraph; //定义图表的结构体类型
typedef struct{
int sno;
int lno;
}keynode; //关键活动
//创建邻接图
void createadj(adjgraph *&g, int a[max][max], int n, int e)
{
arcnode *p;
//分配一段空间,并做相应的赋值操作。
g = (adjgraph *)malloc(sizeof(adjgraph));
g->n = n;
g->e = e;
//对头结点初始化为NULL
for(int i = 0 ;i <n; i++)
{
g->adjlist[i].firstarc = NULL;
g->adjlist[i].count = 0;
}
//循环找结点
for(int i =0; i<n; i++)
for(int j = n-1; j>=0; j--)
if(a[i][j]!=0 && a[i][j]!=inf)//如果有边
{
p = (arcnode *)malloc(sizeof(arcnode));
p->adjvex =j;
p->weight = a[i][j];
g->adjlist[j].count++;
//这里采用头插发
p->nextarc = g->adjlist[i].firstarc;
g->adjlist[i].firstarc = p;
}
}
//输入图的邻接表
void showadj(adjgraph *g)
{
arcnode *p;
for(int i =0; i< g->n; i++)
{
//首先指向头结点。
p = g->adjlist[i].firstarc;
printf("%d 入度:(%d): ",i,g->adjlist[i].count);
//这里是循环输出每个头结点的链表
while(p!=NULL)
{
printf("%d -->",p->adjvex);
p = p->nextarc;
}
printf("/\\");
printf("\n");
}
}
//拓扑排序输出
/*算法的主要思想,一个是找入度为0的顶点进栈,二个每次出栈一个顶点,
就要遍历其他相关有边的顶点,修改顶点入度,并判断是否入栈,不满足就寻找下一个相关有边的顶点。
*/
int tp(adjgraph *g,int toplist[]) //参数数组toplist是用来保存拓扑序列的,用来判断是否成环
{
arcnode *p;
//定义一个数组栈以及数组下标
int st[max];
int top = -1;
int d=0;
int j,k;//定义两个变量,下面需要用到
//这个循环用来遍历所有点,找到入度为0的点将其进栈。
for(int i=0; i<g->n; i++)
if(g->adjlist[i].count ==0)
{
top++;
st[top] = i;
}
//在栈不空的情况下,出栈一个顶点,然后将这个顶点的所有有边的另外一端的顶点循环判断,如果入度为0,则进栈。遍历完之后接着出栈一个顶点,在遍历....循环同样的操作
while(top>-1)
{
//出栈一个顶点
k = st[top--];
toplist[d++]=k;
printf("%d ",k);
//在遍历该出栈顶点的其他有边的顶点
p = g->adjlist[k].firstarc;
while(p!=NULL)
{
//度数-1
j = p->adjvex;
g->adjlist[j].count--;
//在判断是否可以入栈
if(g->adjlist[j].count == 0 )
{
top++;
st[top] =j;
}
//遍历下一个顶点。
p =p ->nextarc;
}
}
return d; //返回拓扑数组的长度 ,注意下标
}
//aoe求关键活动
/*算法的主要思想是求出事件的最早开始时间,(ve[max])
事件的最迟开始时间,(vl[max])
活动的最早开始时间,(ea[max])
活动的最迟开始时间。(el(max))
然后用一个结构体数组保存关键活动以及两端的顶点。
*/
bool aoesearch(adjgraph *g, keynode ky[max], int &d)///结构体数组,以及下标d
{
arcnode *p;
int toplist[max]; //这里用来保存调用拓扑排序函数是保存拓扑序列
int w;
int ve[max]; //用来保存事件(顶点)最早开始时间
int vl[max];//用来保存事件(顶点)最迟开始时间
int length = tp(g,toplist);//拓扑排序
if(length < g->n) //拓扑排序长度若小于顶点数,则说明有回路
return false;
int inode = toplist[0]; //找打源点
int lnode = toplist[g->n-1];//找到汇点
//初始化,每个事件的最早开始时间为0
for(int i=0;i< g->n; i++)
{
ve[i] = 0;
}
//找每个事件的最早开始事件。(找最大)
for(int i =0; i<g->n; i++)
{
p = g->adjlist[i].firstarc;
while(p!=NULL)
{
w = p->adjvex;
if(ve[i]+ p->weight> ve[w])
ve[w] = ve[i]+ p->weight;
p = p->nextarc;
}
}
//初始化,每个事件的最晚开始事件为汇点的最早开始时间
for(int i =0; i<g->n; i++)
vl[i] = ve[lnode];
//逆序找每个事件的最晚开始事件(找最小)
for(int i= g->n-2; i>=0; i--)//
{
p = g->adjlist[i].firstarc;
while(p!=NULL)
{
w = p->adjvex;
if( vl[w] - p->weight <vl[i])
vl[i] = vl[w] - p->weight;
p = p->nextarc;
}
}
//找关键活动((存储到结构体数组中)
for(int i =0; i<g->n; i++)
{
p = g->adjlist[i].firstarc;
while(p!= NULL)
{
w = p->adjvex;
//注意,由于没定义活动(边)的最早开始时间ae[i]和最迟开始时间al[j],这里ae[i] = i,al[i] = vl[w] - p->weight,这里的ae和al表示活动的最早开始时间和最迟开始时间。
if(ve[i] == (vl[w] - p->weight))
{
d++;
ky[d].sno = i;
ky[d].lno = w;
}
p = p->nextarc;
}
}
return true;
}
void showpath(adjgraph *g, keynode ky[],int d)
{
arcnode *p;
int w;
int length =d;
for(int i =0; i<g->n; i++)
{
p = g->adjlist[i].firstarc;
while(p!=NULL)
{
w = p->adjvex;
for(int j =0; j<=length; j++) //这里之所以等于0,是因为d是从-1开始,第一个值的下标就是0,而不是从0开始,第一个值下标是1;
if(i == ky[j].sno && w == ky[j].lno)
{
printf("(%d,%d):%d \n",i,w,p->weight);
}
p = p->nextarc;
}
}
}
int main()
{
adjgraph *b;
int d=-1; //用于结构体数组的下标
keynode ky[max];
int toplist[max];
int a[max][max] = {{0,6,4,5,inf,inf,inf,inf,inf},
{inf,0,inf,inf,1,inf,inf,inf,inf},
{inf,inf,0,inf,1,inf,inf,inf,inf},
{inf,inf,inf,0,inf,inf,inf,2,inf},
{inf,inf,inf,inf,0,9,7,inf,inf},
{inf,inf,inf,inf,inf,0,inf,inf,2},
{inf,inf,inf,inf,inf,inf,0,inf,4},
{inf,inf,inf,inf,inf,inf,inf,0,4},
{inf,inf,inf,inf,inf,inf,inf,inf,0}};
int n= 9;
int e= 11;
createadj(b,a,n,e);
showadj(b);
printf("\n拓扑排序输出序列为: ");
aoesearch(b,ky,d);
printf("\n ");
printf("\n关键活动如下:\n");
showpath(b,ky,d);
return 0;
}
程序运行如下::