1、关键路径问题的简要介绍
关键路径是从工程领域抽象出来的一类问题,这类问题的求解常用于工程完成所需时间的估算,包括完成整个工程至少需要多少时间?其中哪些子工程是影响工程进度的关键?
为求解此类问题,采用如下形式的图结构来表示工程:用弧(有向边)表示活动(子工程);用弧的权值表示活动的持续时间;用弧两端的顶点分别表示活动的开始和结束,叫做事件(即工程中的瞬间行)。这样的有向图称为AOE网(Activity On Edge)。显然,一个能正常进行的工程所对应的AOE网是一个有向无环图。由于整个工程通常有一个唯一的开始时间和结束时间,因此,相应地,在AOE网中分别对应一个顶点,称开始点(即入度为0的点)和结束点(即出度为0的点)。
由于有些活动只能依次进行,有些活动却可以并行进行,所以整个工程所需要的最少时间不是所有活动所需时间的累积,而是从开始点到结束点之间工期最长的一条路径,这条最长的路径即关键路径(Critical Path)。最长路径可能不止一条,在最长路径上的活动即关键活动(Critical Activity)。
2、程序框架设计
2.1、有向图的存储
构建一个graph类,使用邻接链表和逆邻接链表用来存储工程有向图。
graph类框架表
graph |
public |
function |
graph(int x,int y) |
构造函数 |
~graph() |
析构函数 |
|||
void creategraph(group test[]) |
存储有向图 |
|||
void getindegrees(int ind[]) |
获取顶点入度 |
|||
void getoutdegrees(int outd[]) |
获取顶点出度 |
|||
void deletegraph() |
删除有向图 |
|||
data |
int vernum |
顶点数 |
||
int arcnum |
弧数 |
|||
group test[100] |
group类型结构体 |
|||
vnode verlist[100] |
vnode类型结构体 |
graph类使用的结构体框架表
struct |
group 存储每一条弧的起点、终点、事件、时间。 |
int start |
起点 |
int end |
终点 |
||
int thing |
事件编号 |
||
int time |
时间 |
||
enode 用于邻接链表,存储每一顶点邻接点、对应弧时间、邻接点指针 |
int adjver |
邻接点编号 |
|
int data |
时间 |
||
struct enode *enext |
邻接点指针 |
||
cenode 用于逆邻接链表,存储每一顶点前邻接点、对应弧时间、前邻接点指针 |
int adjver |
逆邻接点编号 |
|
int data |
时间 |
||
struct cenode *cenext |
逆邻接点指针 |
||
vnode 存储每一顶点的编号、对应邻接链表头指针、对应逆邻接链表头指针 |
int data |
顶点编号 |
|
enode *ehead |
邻接链表头指针 |
||
cenode *cehead |
逆邻接链表头指针 |
2.2、数据结构链栈
构建一个lstack链栈类,用于主程序使用。
lstack类框架表
lstack |
public |
function |
lstack() |
构造函数 |
~lstack() |
析构函数 |
|||
void pushstack(int x) |
入栈函数 |
|||
int popstack() |
出栈函数 |
|||
data |
snode *top |
snode类型结构体指针 |
lstack类使用的结构体框架表
struct |
snode 链栈数据、指针 |
int data |
压栈数据 |
struct snode *next |
链栈指针 |
2.3、主程序框架
主程序用来求出AOE网中的所有关键活动,由以下几个子程序组成。
邻接链表拓扑排序topo:使用邻接链表来判断AOE网是否有环,以判断是否能够继续求出关键活动,同时求出每个顶点的最早发生时间。
‚逆邻接链表拓扑排序ctopo:使用逆邻接链表求出每个顶点的最迟发生时间。
ƒ关键活动aoe:通过判断每一个顶点的最早发生时间是否等于最迟发生时间求出关键路径上的顶点即事件以及关键活动。
主程序框架表
main program |
function |
bool topo(int x,lstack s,int ind[],int e[],graph g) |
邻接链表拓扑排序 |
void ctopo(int x,lstack s,int outd[],int e[],int l[],graph g) |
逆邻接链表拓扑排序 |
||
void aoe(int x,int y,int cv[],int ca[],lstack s,int outd[],int e[],int l[],graph g) |
求关键活动子程序 |
||
int main() |
主函数,见流程图 |
||
data |
int count |
全局变量,统计关键活动个数 |
|
int x |
有向图顶点数 |
||
int y |
有向图弧数 |
||
int ind[100] |
顶点入度数组 |
||
int outd[100] |
顶点出度数组 |
||
int e[100] |
顶点最早发生时间数组 |
||
int l[100] |
顶点最迟发生时间数组 |
||
int cv[100] |
关键路径上的事件 |
||
int ca[100] |
关键路径上的活动 |
||
lstack s |
lstack类型变量s |
||
graph g(x,y) |
graph类型变量g |
程序具体运行过程中有教导用户如何使用的交互语句,但是为了用户更好的使用,还是特地将步骤以及注意事项介绍如下:
1、用户需要在运行程序之前在该程序所在文件夹目录下,创建一个txt文档,并按照xxx.txt的格式命名该文档。用户需要在该文档中输入所要求解的AOE网的数据,输入的要求见下方“输入数据格式”。
之所以选择将数据输入txt文档,是居于以下几个原因:
考虑到用户所要求的AOE网数据较多,相比于在程序运行过程中输入数据,将数据输入txt文档再由程序读取更为方便。
‚在程序运行过程中输入数据,极有可能由于疏忽输入了错误的数据,于是只能关闭程序,从头再来,浪费时间浪费精力。
ƒ程序运行的结果,在运行界面显示的同时,也会输入到同一txt文档中,便于用户后期查看运行结果。
输入数据格式:
start end activity time
活动起点 活动终点 AOE网中的某一活动 活动时间
附注:每一活动占一行,每个数据之间用空格隔开。
‚所有数据均为数字,即活动、事件的标号均为数字。
ƒ从开始点标号为1开始,按从左到右从上到下的顺序给每个顶点标 号,一直到结束点。
2、运行程序。按照程序运行界面的汉字提示,一步一步运行程序。当程序运行结束了,用户可以查看txt文档来查看运行结果。
3、程序运行的结果,是给出所有的关键活动以及对应数据。
输出数据格式:
activity start end s_earliest_time e_latest_time
关键活动 活动起点 活动终点 起点最早发生时间 终点最迟发生时间
附注:每一活动占一行,每个数据之间用空格隔开。
‚由于关键活动的最早、最迟发生时间相同,故而选择输出关键活动起点最早发生时间,终点最迟发生时间,以用来互相比较。
//AOE
#include
using namespace std;
//define linkedstack
struct snode
{
int data;
struct snode *next;
};
class lstack
{
public:
lstack();
~lstack();
void pushstack(int x);
int popstack();
snode *top;
};
lstack::lstack()
{
top=NULL;
}
lstack::~lstack()
{
}
void lstack::pushstack(int x)
{
snode *s;
s=new snode;
s->data=x;
s->next=top;
top=s;
}
int lstack::popstack()
{
snode *u;
int x;
if(top==NULL)
return 0;
else
{
x=top->data;
u=top;
top=top->next;
delete u;
return x;
}
}
//define graph
struct group
{
int start;
int end;
int thing;
int time;
};
struct enode
{
int adjver;
int data;
struct enode *enext;
};
struct cenode
{
int adjver;
int data;
struct cenode *cenext;
};
struct vnode
{
int data;
enode *ehead;
cenode *cehead;
};
class graph
{
public:
graph(int x,int y);
~graph();
void creategraph(group test[]);
void getindegrees(int ind[]);
void getoutdegrees(int outd[]);
void deletegraph();
int vernum;
int arcnum;
group test[100];
vnode verlist[100];
};
graph::graph(int x,int y)
{
vernum=x;
arcnum=y;
for(int i=1;i<=vernum;i++)
{
verlist[i].data=i;
verlist[i].ehead=new enode;
verlist[i].ehead->enext=NULL;
verlist[i].cehead=new cenode;
verlist[i].cehead->cenext=NULL;
}
}
graph::~graph()
{
}
void graph::creategraph(group test[])
{
for(int j=1;j<=vernum;j++)
{
for(int i=1;i<=arcnum;i++)
{
if(test[i].start==verlist[j].data)
{
enode *etemp;
etemp=new enode;
etemp->adjver=test[i].end;
etemp->data=test[i].time;
etemp->enext=verlist[j].ehead->enext;
verlist[j].ehead->enext=etemp;
}
}
for(int i=1;i<=arcnum;i++)
{
if(test[i].end==verlist[j].data)
{
cenode *cetemp;
cetemp=new cenode;
cetemp->adjver=test[i].start;
cetemp->data=test[i].time;
cetemp->cenext=verlist[j].cehead->cenext;
verlist[j].cehead->cenext=cetemp;
}
}
}
}
void graph::getindegrees(int ind[])
{
cenode *cep;
for(int i=1;i<=vernum;i++)
{
ind[i]=0;
cep=verlist[i].cehead->cenext;
while(cep!=NULL)
{
ind[i]++;
cep=cep->cenext;
}
}
}
void graph::getoutdegrees(int outd[])
{
enode *ep;
for(int i=1;i<=vernum;i++)
{
outd[i]=0;
ep=verlist[i].ehead->enext;
while(ep!=NULL)
{
outd[i]++;
ep=ep->enext;
}
}
}
void graph::deletegraph()
{
for(int i=1;i<=vernum;i++)
{
while(verlist[i].ehead->enext!=NULL)
{
enode *eu;
eu=verlist[i].ehead->enext;
verlist[i].ehead->enext=eu->enext;
delete eu;
}
delete verlist[i].ehead;
while(verlist[i].cehead->cenext!=NULL)
{
cenode *ceu;
ceu=verlist[i].cehead->cenext;
verlist[i].cehead->cenext=ceu->cenext;
delete ceu;
}
delete verlist[i].cehead;
}
}
//main program
#include
#include
#include
using namespace std;
int count;
bool topo(int x,lstack s,int ind[],int e[],graph g);
void ctopo(int x,lstack s,int outd[],int e[],int l[],graph g);
void aoe(int x,int y,int cv[],int ca[],lstack s,int outd[],int e[],int l[],graph g);
int main()
{
cout<<"please build a file named as xxx.txt in the program folder"<>x>>y;
graph g(x,y);
string filename;
cout<<"please input the filename you just built"<>filename;
ifstream infile;
infile.open(filename.c_str());
if(!infile.is_open())
{
cerr<<"cannot read file,please restart"<>g.test[i].start>>g.test[i].end>>g.test[i].thing>>g.test[i].time;
}
infile.close();
cout<<"succeed to read file,please click enter"<=1;i--)
{
cout<enext;
while(eq!=NULL)
{
v2=eq->adjver;
ind[v2]--;
if(e[v1]+eq->data>e[v2])
e[v2]=e[v1]+eq->data;
if(ind[v2]==0)
s.pushstack(v2);
eq=eq->enext;
}
}
if(vcount==x)
return true;
else
return false;
}
void ctopo(int x,lstack s,int outd[],int e[],int l[],graph g)
{
for(int i=x;i>=1;i--)
l[i]=e[x];
int v1,v2;
for(int i=x;i>=1;i--)
{
if(outd[i]==0)
s.pushstack(i);
}
while(s.top!=NULL)
{
v1=s.popstack();
cenode *ceq;
ceq=g.verlist[v1].cehead->cenext;
while(ceq!=NULL)
{
v2=ceq->adjver;
outd[v2]--;
if(l[v1]-ceq->datadata;
if(outd[v2]==0)
s.pushstack(v2);
ceq=ceq->cenext;
}
}
}
void aoe(int x,int y,int cv[],int ca[],lstack s,int outd[],int e[],int l[],graph g)
{
int num=0;
for(int i=x;i>=1;i--)
{
if(e[i]==l[i])
{
num++;
s.pushstack(i);
}
}
count=num;
for(int j=1;j<=num;j++)
cv[j]=s.popstack();
for(int i=1;i<=num;i++)
{
for(int j=1;j<=y;j++)
{
if(g.test[j].start==cv[i])
{
for(int k=1;k<=num;k++)
{
if(g.test[j].end==cv[k])
s.pushstack(j);
}
}
}
}
for(int i=1;i<=num;i++)
ca[i]=s.popstack();
}