Description
关键路径的工期决定了整个项目的工期。任何关键路径上的终端元素的延迟将直接影响项目的预期完成时间(例如在关键路径上没有浮动时间)。
一个项目可以有多个,并行的关键路径。另一个总工期比关键路径的总工期略少的一条并行路径被称为次关键路径。 最初,关键路径方法只考虑终端元素之间的逻辑依赖关系。关键链方法中增加了资源约束。
把工程计划表示为有向图,用顶点表示事件,弧表示活动;
每个事件表示在它之前的活动已完成,在它之后的活动可以开始
AOE网
用顶点表示事件,弧表示活动,弧上的权值表示活动持续的时间的有向图叫AOE(Activity On Edge Network)网 。AOE网常用于估算工程完成时间。例如: 图 1 是一个网。其中有9个事件v1,v2,…,v9;11项活动a1,a2,…,a11。每个事件表示在它之前的活动已经完成,在它之后的活动可以开始。如 v1表示整个工程开始,v9 表示整个工程结束。V5表示活动,a4和a5已经完成,活动a7和a8可以开始。与每个活动相联系的权表示完成该活动所需的时间。如活动a1需要6天时间 可以完成。AOE 网具有的性质
只有在某顶点所代表的事件发生后,从该顶点出发的各有向边所代表的活动才能开始。 只有在进入某一顶点的各有向边所代表的活动都已经结束,该顶点所代表的事件才能发生。 表示实际工程计划的AOE网应该是无环的,并且存在唯一的入度过为0的开始顶点和唯一的出度为0的完成顶点。hant
定义
AOE网(Activity On Edge)——也叫边表示活动的网。AOE网是一个带权的有向无环图,其中顶点表示事件,弧表示活动,权表示活动持续时间
路径长度——路径上各活动持续时间之和
关键路径——路径长度最长的路径叫~
Ve(j)——表示事件Vj的最早发生时间
Vl(j)——表示事件Vj的最迟发生时间
e(i)——表示活动ai的最早开始时间
l(i)——表示活动ai的最迟开始时间
l(i)-e(i)——表示完成活动ai的时间余量
关键活动——关键路径上的活动叫~,即l(i)=e(i)的活动
求关键路径步骤 :
求Ve(i)
求Vl(j)
求e(i)
求l(i)
计算l(i)-e(i)
算法实现
以邻接表作存储结构
从源点V1出发,令Ve[1]=0,按拓扑序列求各顶点的Ve[i]
从汇点Vn出发,令Vl[n]=Ve[n],按逆拓扑序列求其余各顶点的Vl[i]
根据各顶点的Ve和Vl值,计算每条弧的e[i]和l[i],找出e[i]=l[i]的关键活动
关键路径的几个术语 (1) 关键路径 从源点到汇点的路径长度最长的路径叫关键路径。
(2) 活动开始的最早时间e(i)
(3) 活动开始的最晚时间l(i) 定义e(i)=l(i)的活动叫关键活动。
(4) 事件开始的最早时间ve(i)
(5) 事件开始的最晚时间vl(i)
设活动ai由弧<j,k>(即从顶点j到k)表示,其持续时间记为dut(<j,k>),则
e(i)=ve(j) l(i)=vl(k)-dut(<j,k>) (6_6_1)
求ve(i)和vl(j)分两步:
· 从ve(1)=0开始向前递推 ve(j)=Max{ ve(i)+dut(<i,j>) } (6_6_2) <i,j>T, 2<=j<=n 其中T是所有以j为弧头的弧的集合。
· 从vl(n)=ve(n)开始向后递推 vl(i)=Min{ vl(j)-dut(<i,j>) } (6_6_3) <i,j>S, 1<=i<=n-1 其中S是所有以i为弧尾的弧的集合。
两个递推公式是在拓扑有序和逆拓扑有序的前提下进行。
求关键路径的算法
(1) 输入e条弧<j,k>,建立AOE网的存储结构。
(2) 从源点v1出发,令ve(1)=0,求 ve(j) 2<=j<=n。
(3) 从汇点vn出发,令vl(n)=ve(n),求 vl(i) 1<=i<=n-1。
(4) 根据各顶点的ve和vl值,求每条弧s(活动)的最早开始时间e(s)和最晚开始时间l(s),其中e(s)=l(s)的为关键活动。 求关键路径是在拓扑排序的前提下进行的,不能进行拓扑排序,自然也不能求关键路径。
求关键路径的算法分析
(1) 求关键路径必须在拓扑排序的前提下进行,有环图不能求关键路径;(2) 只有缩短关键活动的工期才有可能缩短工期;
(3) 若一个关键活动不在所有的关键路径上,减少它并不能减少工期;
(4) 只有在不改变关键路径的前提下,缩短关键活动才能缩短整个工期。
co
#include
<
stdio.h
>
#include < malloc.h >
#define M 20
#define MAX 100
typedef struct node
{ int vex;
int length;
struct node * next;
}JD;
typedef struct tnode
{ int vexdata;
int in ;
struct node * link;
}TD;
int loc_vertex(TD g[], int vex, int n) // 定位输入结点的存储坐标
{ int i,j;
for (i = 1 ;i <= n;i ++ )
if (g[i].vexdata == vex)
return (i);
return ( 0 );
}
int crt_linklist(TD g[]) // 建立邻接链表
{ int n,e,i,j,k,vt,vh,length;
JD * p;
printf( " Input and the number of node,arc: " );
scanf( " %d,%d " , & n, & e);
for (i = 1 ;i <= n;i ++ ) // 输入结点的信息
{ printf( " g[%d].vexdata= " ,i);
scanf( " %d " , & g[i].vexdata);
g[i]. in = 0 ;
g[i].link = NULL;
}
for (k = 1 ;k <= e;k ++ ) // 输入边的信息
{ printf( " Vt,Vh,length: " );
scanf( " %d,%d,%d " , & vt, & vh, & length);
i = loc_vertex(g,vt,n);
j = loc_vertex(g,vh,n);
p = (JD * )malloc( sizeof (JD));
p -> vex = j;
p -> length = length;
p -> next = g[i].link;
g[i].link = p;
}
return (n);
}
void cal_in(TD g[], int n) // 计算每个结点的入度
{ int i,k;
JD * p;
for (i = 1 ;i <= n;i ++ )
{ p = g[i].link;
while (p != NULL)
{ k = p -> vex;
g[k]. in ++ ;
p = p -> next;
}
}
}
int dut(TD g[], int vt, int vh) // 返回每个结点之间的边的权值
{ JD * p;
p = g[vt].link;
while (p != NULL)
{ if (p -> vex == vh)
return (p -> length);
else
p = p -> next;
}
return (MAX);
}
int toporder(TD g[], int n, int ve[], int top2[], int * t2) // 作用在于求ve[k],和为后面的求vl[k]
{ int top1[M];
int m,k,j,top;
JD * p;
top = 0 ; m = 0 ;
for (j = 1 ;j <= n;j ++ ) // 初始化事件(结点)的最早开始时间,方法二:memset(ve,0,sizeof(ve));
ve[j] = 0 ;
for (j = 1 ;j <= n;j ++ )
if (g[j]. in == 0 )
{ top1[top] = j;
top ++ ;
}
while (top > 0 )
{ j = top1[ -- top];
top2[ * t2] = j; // t2初值为零
( * t2) ++ ;
m ++ ;
p = g[j].link;
while (p != NULL)
{ k = p -> vex;
g[k]. in -- ;
if (g[k]. in == 0 )
top1[top ++ ] = k;
if (ve[j] + dut(g,j,k) > ve[k]) // 求ve[k],ve[k]=ve[j]+dut(g,j,k);(ve[1]=0;)
ve[k] = ve[j] + dut(g,j,k);
p = p -> next;
}
}
if (m < n) return ( 0 );
else return ( 1 );
}
void critical_path(TD g[], int n)
{ int i,t2 = 0 ,j,k,ee,el;
char tag;
JD * p;
int ve[M],vl[M],top2[M];
i = toporder(g,n,ve,top2, & t2);
if ( ! i)
printf( " Has a cycle! " );
else
{ for (i = 1 ;i <= n;i ++ )
vl[i] = MAX;
/// * 求出vl[k]* ///
vl[n] = ve[n];
while (t2 > 0 )
{ j = top2[ -- t2];
p = g[j].link;
while (p != NULL)
{ k = p -> vex;
if (vl[k] - dut(g,j,k) < vl[j])
vl[j] = vl[k] - dut(g,j,k);
p = p -> next;
}
}
/// * 求出活动的的e[k],l[k]* ///
for (j = 1 ;j <= n;j ++ )
{ p = g[j].link;
while (p != NULL)
{ k = p -> vex;
ee = ve[j];
el = vl[k] - dut(g,j,k);
if (ee == el)
tag = ' * ' ; // 表示关键路径
else
tag = ' ' ;
printf( " Vt=%d,Vh=%d,Length=%d,ee=%d,el=%d,%c\n " ,j,k,dut(g,j,k),ee,el,tag);
p = p -> next;
}
}
}
}
int main()
{ int n;
TD g[M];
n = crt_linklist(g);
cal_in(g,n);
critical_path(g,n);
return ( 0 );
}
#include < malloc.h >
#define M 20
#define MAX 100
typedef struct node
{ int vex;
int length;
struct node * next;
}JD;
typedef struct tnode
{ int vexdata;
int in ;
struct node * link;
}TD;
int loc_vertex(TD g[], int vex, int n) // 定位输入结点的存储坐标
{ int i,j;
for (i = 1 ;i <= n;i ++ )
if (g[i].vexdata == vex)
return (i);
return ( 0 );
}
int crt_linklist(TD g[]) // 建立邻接链表
{ int n,e,i,j,k,vt,vh,length;
JD * p;
printf( " Input and the number of node,arc: " );
scanf( " %d,%d " , & n, & e);
for (i = 1 ;i <= n;i ++ ) // 输入结点的信息
{ printf( " g[%d].vexdata= " ,i);
scanf( " %d " , & g[i].vexdata);
g[i]. in = 0 ;
g[i].link = NULL;
}
for (k = 1 ;k <= e;k ++ ) // 输入边的信息
{ printf( " Vt,Vh,length: " );
scanf( " %d,%d,%d " , & vt, & vh, & length);
i = loc_vertex(g,vt,n);
j = loc_vertex(g,vh,n);
p = (JD * )malloc( sizeof (JD));
p -> vex = j;
p -> length = length;
p -> next = g[i].link;
g[i].link = p;
}
return (n);
}
void cal_in(TD g[], int n) // 计算每个结点的入度
{ int i,k;
JD * p;
for (i = 1 ;i <= n;i ++ )
{ p = g[i].link;
while (p != NULL)
{ k = p -> vex;
g[k]. in ++ ;
p = p -> next;
}
}
}
int dut(TD g[], int vt, int vh) // 返回每个结点之间的边的权值
{ JD * p;
p = g[vt].link;
while (p != NULL)
{ if (p -> vex == vh)
return (p -> length);
else
p = p -> next;
}
return (MAX);
}
int toporder(TD g[], int n, int ve[], int top2[], int * t2) // 作用在于求ve[k],和为后面的求vl[k]
{ int top1[M];
int m,k,j,top;
JD * p;
top = 0 ; m = 0 ;
for (j = 1 ;j <= n;j ++ ) // 初始化事件(结点)的最早开始时间,方法二:memset(ve,0,sizeof(ve));
ve[j] = 0 ;
for (j = 1 ;j <= n;j ++ )
if (g[j]. in == 0 )
{ top1[top] = j;
top ++ ;
}
while (top > 0 )
{ j = top1[ -- top];
top2[ * t2] = j; // t2初值为零
( * t2) ++ ;
m ++ ;
p = g[j].link;
while (p != NULL)
{ k = p -> vex;
g[k]. in -- ;
if (g[k]. in == 0 )
top1[top ++ ] = k;
if (ve[j] + dut(g,j,k) > ve[k]) // 求ve[k],ve[k]=ve[j]+dut(g,j,k);(ve[1]=0;)
ve[k] = ve[j] + dut(g,j,k);
p = p -> next;
}
}
if (m < n) return ( 0 );
else return ( 1 );
}
void critical_path(TD g[], int n)
{ int i,t2 = 0 ,j,k,ee,el;
char tag;
JD * p;
int ve[M],vl[M],top2[M];
i = toporder(g,n,ve,top2, & t2);
if ( ! i)
printf( " Has a cycle! " );
else
{ for (i = 1 ;i <= n;i ++ )
vl[i] = MAX;
/// * 求出vl[k]* ///
vl[n] = ve[n];
while (t2 > 0 )
{ j = top2[ -- t2];
p = g[j].link;
while (p != NULL)
{ k = p -> vex;
if (vl[k] - dut(g,j,k) < vl[j])
vl[j] = vl[k] - dut(g,j,k);
p = p -> next;
}
}
/// * 求出活动的的e[k],l[k]* ///
for (j = 1 ;j <= n;j ++ )
{ p = g[j].link;
while (p != NULL)
{ k = p -> vex;
ee = ve[j];
el = vl[k] - dut(g,j,k);
if (ee == el)
tag = ' * ' ; // 表示关键路径
else
tag = ' ' ;
printf( " Vt=%d,Vh=%d,Length=%d,ee=%d,el=%d,%c\n " ,j,k,dut(g,j,k),ee,el,tag);
p = p -> next;
}
}
}
}
int main()
{ int n;
TD g[M];
n = crt_linklist(g);
cal_in(g,n);
critical_path(g,n);
return ( 0 );
}