**
如何求关键活动以及存在多条关键路径的输出(数据结构C语言版)
**
1.问题描述
关键路径:
通常把计划、施工过程、生产流程、程序流程等都当成一个工程。工程通常分为若干个称为“活动”的子工程。完成了这些“活动”,这个工程就可以完成了。通常用 AOE-网来表示工程。AOE-网是一个带权的有向无环图,其中,顶点表示事件(EVENT),弧表示活动,权表示活动持续的时间。
AOE-网可以用来估算工程的完成时间。可以使人们了解:
(1)研究某个工程至少需要多少时间?
(2)哪些活动是影响工程进度的关键?
由于 AOE-网中的有些活动可以并行进行,从开始点到各个顶点,以致从开始点到完成点的有向路径可能不止一条,这些路径的长度也可能不同。完成不同路径的活动所需的时间虽然不同,但只有各条路径上所有活动都完成了,这个工程才算完成。因此,完成工程所需的最短时间是从开始点到完成点的最长路径的长度,即在这条路径上的所有活动的持续时间之和.这条路径长度就叫做关键路径(Critical Path)。
例:AOE 图如下:
程序执行结束后应该输出:
关键活动为 a1,a4,a7,a10,a8,a11
关 键 路 径 为 : a1->a4->a7->a10 ( 或 者 V1->V2->V5->V7->V9 ) 和a1->a4->a8->a11(或者 V1->V2->V5->V8->V9)
花费的时间为至少为 18(时间单位)。
1.算法设计
采用邻接表存储图的信息,表头节点存储各顶点的命名、序号和指向第一条依附该顶点的弧的指针,表节点存储依附顶点的边的信息(弧头顶点,对应的顶点位置,权重),在输入数据时采用尾插法将节点链域插入到表节点的fristarc域。
假设起点是VO,则称从VO到Vi的最长路径的长度为Vi的最早发生时间,同时,Vi的最早发生时间也是所有以Vi为尾的的弧所表示的活动的最早开始时间,使用ee(i)表示活动ai最早发生时间,除此之外,还定义一个活动最迟发生时间,使用el(i)表示,不推迟工期的最晚开工时间,把ee(i)=el(i)的活动ai称为关键活动。
ee(i)=ve(j)
el(i)=vl(k)-dut(<j,k>)
ve(j)代表的是弧尾j的最早发生时间;
Vl(k)代表的是弧头k的最迟发生时间;
dut(<j,k>)代表活动要持续的时间,既是弧的权值;
在知道ee(i)和el(i)前必须知道各个顶点的ve和vl
ve(j)=max{ve(i)+dut<i,j>}
<i,j>是所有以第j个顶点为头的弧的集合,按拓扑有序求各顶点的最早发生时间;
Vl(i)=min{vl(j)-dut(i,j)}
<i,j>是所有以第i个顶点为尾的弧的集合,按逆拓扑有序求各顶点的最迟发生时间;
求关键路径的过程:
用二维数组label[i][j]来表示<i,j>弧之间是否存在最短路径,值为1则表示存在,为0不存在,在循环确定ee=el时,将label[j][k]置1。
Mark[]数组用来记录关键路径的上的活动项目。
开始时先将开始项目存进mark[]中,然后以开始项目进行递归,直到找到结束项目时则表明mark[]数组中已经保存了一条完整的关键路径,输出关键路径;否则判断两点之间是否存在关键路径,既label[][]=1,存在时将label[][]置0,表示当前活动项目不可再次选择;并将当前项目保存进mark数组中。
如果找到一条关键路径后,每当返回到上一层递归并将mark数组最后一个项目弹出,并将label[][]置1表示当前项目可以重新选择,在具有多条关键活动的项目后重新选择新的关键路径项目点,直到所有的关键路径输出完递归结束,即可求出多条关键路径。
2.算法实现
流程图:
源代码
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define max 100
#define stack_size 100
#define stackincreasing 10
int indegree[20] = { 0 }, ve[20] = { 0 }; //顶点的入度表以及最迟发生时间
int label[max][max]={0};// 用来<i,j>是否存在关键路径,值为0不存在,为1存在
int mark[max],m=2;//存储从开始项目到结束项目的单条关键路径
//栈的数据结构
typedef struct stack
{
int *front;
int *top;
int stacksize;
}stack;
//采用表邻接存储
typedef struct arcnode
{ int path_num;
int adj;
char names[20];
struct arcnode *next;
int info;
}arcnode;
typedef struct
{ int xuhao;
char name[20];
arcnode *firstnode;
}vnode, adjlist[max];
typedef struct
{ int vexnum;
int begin;
int finsh;
adjlist arcs;
}mgraph;
stack lnitstack();//栈的初始化
void push(stack &s, int w);//插入元素
void pop(stack &s, int &w);//弹出元素
int emptystack(stack &s);//判断栈是否为空
void menu();//开始界面
void select( mgraph* p);//函数界面
mgraph *creatgraph();//图的建立
int topologicalorder(mgraph* p, stack& t);//求拓扑排序,并入栈
void criticalpath(mgraph *p, stack& s);//求关键路径
void DFS(int x);//深度遍历输出关键路径
stack lnitstack()
{
stack s;
s.front = (int *)malloc(stack_size * sizeof(int));
if (!s.front) exit(0);
s.top = s.front;
s.stacksize = stack_size;
return s;
}
void push(stack &s, int w)
{
if(s.top - s.front >= s.stacksize)
{
s.front = (int *)realloc(s.front, (s.stacksize + stackincreasing) * sizeof(int));
if (!s.front) exit(0);
s.top = s.front + s.stacksize;
s.stacksize += stackincreasing;
}
*s.top++ = w;
}
void pop(stack &s, int &w)
{
if (s.front == s.top) exit(0);
w = *--s.top;
}
int emptystack(stack &s)
{
if (s.top == s.front)
return 0;
else return 1;
}
void menu()
{
printf("\n\n\n\n\n\n\n\n\n\n\n\n");
printf("\t\t\t\t\t*****************\n");
printf("\t\t\t\t\t 关键路径\n");
printf("\t\t\t\t\t*****************\n");
printf("\n\n\n\n\t\t\t\t\t\t\t");
system("pause");
system("CLS");
}
void select( mgraph* p)
{ int se;
printf("\n\n\n\n\n\n\n\n\n\n\n");
printf("\t\t\t\t1-构造关键路劲邻接矩表\n");
printf("\t\t\t\t2-求关键路径以及关键活动并输出\n");
printf("\t\t\t\t4-退出\n");
printf("\n\n\t\t\t\t请选择相应功能的序号:");
while(scanf("%d",&se)==1)
{
switch(se)
{
case 1: system("CLS");
p = creatgraph();
break;
case 2:
system("CLS");
printf("\n\n\n\n\n\n\n\n\n\t\t\t\t");
stack t;
t = lnitstack();
criticalpath(p, t);
break;
}
}
printf("\n\n\n\n\t\t\t\t\t");
system("pause");
system("CLS");
}
void DFS(int x,mgraph *p)
{ if(x ==p->finsh )//递归结束条件是遇到结束项目表示已生成一条完整的关键路径,输出
{
int v;
for(int i=1;i<m;i++)
{
v=mark[i];
printf("%s ",p->arcs [v].name);
}
printf("\n\t");
}
else for(arcnode* q = p->arcs[x].firstnode->next;q;q=q->next )
{
if(label[p->arcs [x].xuhao][q->adj ]==1)//若<i,j>之间存在关键路径
{
label[p->arcs [x].xuhao][q->adj ]=0;//不可重复选择关键路径上的点
mark[m++]=q->adj;//记录此条关键路径上的点
DFS(q->adj ,p);//遍历递归
label[p->arcs [x].xuhao][q->adj ]=1;//还原
m--;//返回上层递归在具有多条关键活动的项目后重新选择新的一条关键路径项目点
}
}
}
mgraph *creatgraph()
{
int m;
mgraph * p;
char begin_name[20];
char finsh_name[20];
p = (mgraph*)malloc(sizeof(mgraph));
printf("请输入总的顶点数:");
scanf("%d", &p->vexnum);
printf("请输入活动的名称:\n");
for (int i = 1; i <= p->vexnum; i++)
{
scanf("%s",p->arcs[i].name);
p->arcs[i].xuhao=i;
p->arcs[i].firstnode = NULL;
}
printf("请输入开始项目名称和结束项目名称:\n");
scanf("%s %s",&begin_name,&finsh_name);
for(int k=1;k<=p->vexnum;k++)
{
if(strcmp(begin_name,p->arcs[k].name)==0 )
{
p->begin=k;
}
else if(strcmp(finsh_name,p->arcs[k].name)==0 )
{
p->finsh=k;
}
}
printf("请输入顶点信息:\n");
arcnode *q, *g, *node;
int num=0;
for (int i = 1; i <=p->vexnum; i++)
{
node = (arcnode*)malloc(sizeof(arcnode));
g = node;
printf("输入%s顶点总的边数目:\n",p->arcs[i].name);
scanf("%d", &m);
for (int j = 0; j < m; j++)
{
int h;
printf("输入%s第%d条边的信息(该弧所指向顶点以及对应的位置以及权重)\n",p->arcs[i].name, j + 1);
q = (arcnode*)malloc(sizeof(arcnode));
scanf("%s %d",&q->names,&h);
for(int k=1;k<=p->vexnum;k++)
{
if(strcmp(q->names,p->arcs[k].name)==0 )
{
q->adj =k;
break;
}
}
q->path_num =++num;
q->info = h;
g->next = q;
g = q;
}
g->next = NULL;
p->arcs[i].firstnode = node;
}
printf("构造成功,再按一次返回选择界面\n");
system("pause");
system("CLS");
select(p);
return p;
}
int topologicalorder(mgraph* p, stack& t)
{
stack s;
arcnode *q;
for (int i = 1; i <= p->vexnum ; i++)
{
q = p->arcs[i].firstnode;
while (q->next)
{
q = q->next;
int m = q->adj;
indegree[m]++;
}
}
s = lnitstack();
for (int i = 1; i <=p->vexnum ; i++)
{
if (indegree[i] == 0)
{
push(s, i);
}
}
int count = 0, j;
while (emptystack(s))
{
pop(s, j);
push(t, j);
++count;
for (q = p->arcs[j].firstnode->next; q; q = q->next)
{
int m = q->adj;
if (--indegree[m] == 0)
{
push(s, m);
}
if (ve[j] + q->info > ve[m])
{
ve[m] = ve[j] +q->info;
}
}
}
if (count < p->vexnum ) return 0;
else return 1;
}
void criticalpath(mgraph *p, stack& s)
{
int vl[20]={0};
arcnode *q;
if (!topologicalorder(p, s))//有环路就返回
{
printf("有环路或者无初始开始项目\n");
printf("\n\n\n\t\t\t\t再按一次返回函数界面\n");
system("pause");
system("CLS");
select(p);
}
for (int i = 1; i <=p->vexnum ; i++)
{
vl[i] = ve[p->finsh ];//所有顶点的最迟发生时间初始化为结束项目的最早发生时间
}
while (emptystack(s))
{
int j;
for (pop(s, j), q = p->arcs[j].firstnode->next; q; q = q->next)
{
int k = q->adj;
int dut = q->info;
if (vl[k] - dut <vl[j])
vl[j] = vl[k] - dut;
}
}
printf("关键活动为 ");
for (int i = 1; i <=p->vexnum ; i++)
{ int j=0;
for (q = p->arcs[i].firstnode->next; q; q = q->next)
{
int k = q->adj;
int dut = q->info;
int ee = ve[i];
int el = vl[k] - dut;
if (ee == el)
{
printf(" a%d,",q->path_num);//输出关键活动
label[p->arcs [i].xuhao][q->adj]=1 ;//<i,j>之间若关键路径则置1
}
}
}
printf("\n关键路径为: ");
mark[1]=p->begin ;//将开始项目先记录入数组
DFS(p->arcs [1].xuhao,p); //深度递归搜索输出关键路径
}
int main()
{ mgraph* p;
menu();
select(p);
return 0;
}