1、首先鼠标左键双击打开MicrosoftProject应用程序。
2、打开需要处理的Project文件“Project1”。
3、接下来点击选择最上面的“FORMAT”设置选项。
4、勾选关键任务“Critical Tasks”,此时可以看到关键任务已经显示出来(粉红色),有左到右的一条粉色任务组成的路径即为关键路径。
5、下图说明,缩短关键路径外的任务“3.1Task”工期(从20天改为10天),总的工期100天没有变化。
6、下图说明,缩短关键路径上的任务工期2.2.3Task(从20天改为15天),总的工期也同等缩短了5天。
/*AOE网研究的问题
(1) 完成整个工程至少需要多少时间;
(2) 哪些活动是影响工程的关键。
注意:该程序可以计算多条关键路径的情况,
只是输出有些不仅人意
数据文件aaa.txt的内容
A B 1
A F 3
A H 4
B C 2
F C 6
H I 4
C D 3
C G 4
I G 1
D E 5
G E 3
*/
#include<iostream>
#include<fstream>
using namespace std
#define N 9
#define MAX 10000
int g[N][N]/*存储图,有向图,有权*/
int into[N]/*存储入度*/
int ans[N]/*存储结果序列,模拟栈*/
int ve[N]/*点的最早时间 ve[j]=Max(ve[k]+g[k][j]) */
int vl[N]/*点的最迟时间 vl[i]=Min(v[k]-g[i][k]) */
//int ee[N*N]/*边的最早时间 ee[m]=ve[i]*/
//int el[N*N]/*边的最迟时间 el[m]=vl[j]-g[i][j]*/
bool topoSort()
{
/*初始化*/
for(int i=0i<Ni++)
{
into[i]=-1
ans[i]=-1
ve[i]=0/*初始化ve*/
}
/*计算入度*/
for(int i=0i<Ni++)
{
for(int j=0j<Nj++)
{
if(g[i][j]<MAX) into[j]++
}
}
/*零入度顶点入栈*/
int top=0/*栈顶指针*/
for(int i=0i<Ni++)
{
int j=0
while(0!=into[j])
{
j++
if(j>N)
{
cout<<"晕!有环"<<endl
return false
}
}
/*进入队列*/
ans[i]=j
into[j]=-1
/*计算Ve*/
for(int k=0k<=ik++)
{
if(g[ans[k]][j] <MAX)/*寻找前驱,k是ans中的标号*/
{
cout<<"ans["<<(char(k+'A'))<<"]= "<<ans[k]<<" "
if(ve[j] <ve[ans[k]]+g[ans[k]][j])/*更新数据 ve[j]=Max(ve[k]+g[k][j])*/
{
ve[j] = ve[ans[k]]+g[ans[k]][j]
cout<<"ve["<<(char(j+'A'))<<"]= "<<ve[j]<<endl
}
}
}
cout<<endl<<endl
for(int k=0k<Nk++)
{
if(MAX >g[j][k])
into[k]--
}
}
for(int m=0m<Nm++)
{
cout<<char(ans[m]+'A')<<" "
}
cout<<endl
for(int m=0m<Nm++)
{
cout<<ve[m]<<" "
}
cout<<endl
/*计算vl, vl[i]=Min(v[k]-g[i][k])*/
/*初始化vl*/
int temp=ve[ans[N-1]]
for(int m=0m<Nm++)
{
vl[m]=temp
}
/*计算*/
for(int i=N-1i>=0i--)
{
for(int k=ik<Nk++)
{
if(g[ans[i]][ans[k]] <MAX)/*是后继,k和i都是ans中的标号*/
{
if(vl[ans[i]] >vl[ans[k]]-g[ans[i]][ans[k]])/*更新数据*/
{
vl[ans[i]] = vl[ans[k]]-g[ans[i]][ans[k]]
}
}
}
}
for(int m=0m<Nm++)
{
cout<<vl[m]<<" "
}
cout<<endl
/*找关键点,<i,j>ve[i]==vl[j]-g[i][j]*/
for(int i=0i<N++i)
{
for(int j=ij<Nj++)
{
if(g[ans[i]][ans[j]] <MAX &&ans[i]!=ans[j])/*存在一条边<i,j>*/
{
/*剩余时间为零,该边在关键路径上,其两个顶点为关键点*/
//cout<<vl[ans[i]]<<" "<<g[ans[i]][ans[j]]<<" "<<ve[ans[i]]<<endl
if(vl[ans[j]]-g[ans[i]][ans[j]]==ve[ans[i]])
{
cout<<"<"<<char(ans[i]+'A')<<" , "<<char(ans[j]+'A')<<"> "
}
}
}
}
cout<<endl
return true
}
int main()
{
/*输入数据*/
fstream fin("aaa.txt")
if(!fin)
{
cerr<<"Cannot open aaa.txt"<<endl
}
/*初始化邻接表*/
for(int m=0m<Nm++)
{
for(int n=0n<Nn++)
{
if(m==n) g[m][n]=0
else g[m][n]=MAX
}
}
//memset(g, MAX, sizeof(g))
char i,j
int k
while(fin>>i>>j>>k)
{
g[(int)(i-'A')][(int)(j-'A')]=k
}
for(int m=0m<Nm++)
{
for(int n=0n<Nn++)
cout<<g[m][n]<<" "
cout<<endl
}
/*拓扑排序*/
int noRing=topoSort()
if(!noRing)
{
return 0
}
return 1
}
关键路径的算法是建立在拓扑排序的基础之上的,这个算法中用到了拓扑排序。
1. 什么是拓扑排序?
举个例子先:一个软件专业的学生学习一系列的课程,其中一些课程必须再学完它的基础的先修课程才能开始。如:在《程序设计基础》和《离散数学》学完之前就不能开始学习《数据结构》。这些先决条件定义了课程之间的领先(优先)关系。这个关系可以用有向图更清楚地表示。图中顶点表示课程,有向边表示先决条件。若课程i是课程j的先决条件,则图中有弧<i,j>。若要对这个图中的顶点所表示的课程进行拓扑排序的话,那么排序后得到的序列,必须是按照先后关系进行排序,具有领先关系的课程必然排在以它为基础的课程之前,若上例中的《程序设计基础》和《离散数学》必须排在《数据结构》之前。进行了拓扑排序之后的序列,称之为拓扑序列。
2. 如何实现拓扑排序?
很简单,两个步骤:
1. 在有向图中选一个没有前驱的顶点且输出。
2. 从图中删除该顶点和以它为尾的弧。
重复上述两步,直至全部顶点均已输出,或者当前图中不存在无前驱的顶点为止。后一种情况则说明有向图中存在环。
3. 什么是关键路径?
例子开头仍然,图1是一个假想的有11项活动的A0E-网。其中有9个事件v1,v2......,v9,每个事件表示在它之前的活动一完成,在它之后的活动可以开始。如v1表示整个工程的开始,v9表示整个工程结束,v5表示a4和a5已完成,a7和a8可以开始。与每个活动相联系的数是执行该活动所需的时间。比如,活动a1需要6天,a2需要4天。
由于整个工程只有一个开始点和一个完成点,故在正常情况(无环)下,网中只有一个入度为零的点(称作源点)和一个出度为零的点(叫做汇点)。
那么该工程待研究的问题是:1.完成整项工程至少需要多少时间?2.哪些活动是影响工程进度的关键?
由于在AOE-网中有些活动可以并行进行,所以完成工程的最短时间是从开始点到完成点的最长路径的长度(这里所说的路径长度是指路径上各活动持续时间之和,不是路径上弧的数目)。路径长度最长的路径叫做关键路径(Critical path)。
假设开始点是v1,从v1到vi的最长路径叫做时间vi的最早发生时间。这个时间决定了所有以vi为尾的弧所表示的活动的最早开始时间。我们用e(i)表示活动ai的最早开始时间。还可以定义一个活动开始的最迟时间l(i),这是在不推迟整个工程完成的前提下,活动ai最迟必须开始进行的时间。两者之差l(i)-e(i)意味着完成活动ai的时间余量。当这个时间余量等于0的时候,也即是l(i)=e(i)的活动,我们称其为关键活动。显然,关键路径上的所有活动都是关键活动,因此提前完成非关键活动并不能加快工程的进度。
因此,分析关键路径的目的是辨别哪些是关键活动,以便争取提高关键活动的功效,缩短整个工期。
4. 如何实现关键路径?
由上面的分析可知,辨别关键活动就是要找e(i)=l(i)的活动。为了求得e(i)和l(i),首先应求得事件的最早发生时间ve(j)和最迟发生时间vl(j)。如果活动ai由弧<j,k>表示,其持续时间记为dut(<j,k>),则有如下关系
e(i) = ve(j)
l(i) = vl(k) - dut(<j,k>)
求解ve(j)和vl(j)需分两个步进行:
1) 从ve(0)=0开始向前推进求得ve(j)
Ve(j) = Max{ve(i) + dut(<i,j>) }<i,j>属于T,j=1,2...,n-1
其中T是所有以第j个顶点为头的弧的集合。
2) 从vl(n-1) = ve(n-1)起向后推进求得vl(j)
vl(i) = Min{vl(j) - dut(<i,j>}<i,j>属于S,i=n-2,...,0
其中,S是所有以第i个顶点为尾的弧的集合。
这两个递推公式的计算必须分别在拓扑有序和逆拓扑有序的前提先进行。也就是说,ve(j-1)必须在vj的所有前驱的最早发生时间求得之后才能确定,而vl(j-1)必须在Vj的所有后继的最迟发生时间求得之后才能确定。因此可以在拓扑排序的基础上计算ve(j-1)和vl(j-1)。
具体算法描述如下:
1. 输入e条弧<j,k>,建立AOE-网的存储结构。
2. 拓扑排序,并求得ve[]。从源点V0出发,令ve[0]=0,按拓扑有序求其余各顶点的最早发生时间ve[i]。如果得到的拓扑有序序列中顶点个数小于网中顶点数n,则说明网中存在环,不能求关键路径,算法终止;否则执行步骤3。
3. 拓扑逆序,求得vl[]。从汇点Vn出发,令vl[n-1] = ve[n-1],按逆拓扑有序求其余各顶点的最迟发生时间vl[i]。
4. 求得关键路径。根据各顶点的ve和vl值,求每条弧s的最早开始时间e(s)和最迟开始时间l(s)。若某条弧满足条件e(s) = l(s),则为关键活动。
为了能按逆序拓扑有序序列的顺序计算各个顶点的vl值,需记下在拓扑排序的过程中求得的拓扑有序序列,这就需要在拓扑排序算法中,增设一个栈,以记录拓扑有序序列,则在计算求得各顶点的ve值之后,从栈顶到栈底便为逆拓扑有序序列。
package graphimport java.util.*
public class Grph_CriticalPath
{
Graph_AdjList adjList
Stack<Integer> T = new Stack<Integer>()
int ve[]
int vl[]
final int max = 10000
public Grph_CriticalPath(Graph_AdjList adjList) //图的存储结构是用的邻接表
{
this.adjList = adjList
int length = adjList.vetexValue.length
ve = new int[length]
vl = new int[length]
for(int i=0i<lengthi++)
{
ve[i] = 0
vl[i] = max
}
}
public void getCriticalPath()
{
topologicalOrder()
int t = T.pop()
T.push(t)
vl[t] = ve[t]
while(!T.isEmpty())
{
int j = T.pop()
for(Graph_AdjList.ArcNode p = adjList.vetex[j].firstArc p!=null p = p.next)
{
int k = p.adjvex
if(vl[k]-p.weight<vl[j])
{
vl[j] = vl[k]-p.weight
}
}
}
for(int i=0i<ve.lengthi++)
{
for(Graph_AdjList.ArcNode p = adjList.vetex[i].firstArc p!=null p = p.next)
{
int k = p.adjvex
int ee = ve[i]
int el = vl[k]-p.weight
if(ee==el)
{
System.out.print(i+","+k+" ")
}
}
}
}
public void topologicalOrder()
{
Stack<Integer> S = new Stack<Integer>()
S.push(0)
int count = 0
while(!S.isEmpty())
{
int j = S.pop()
T.push(j)
count++
Graph_AdjList.ArcNode p = null
for(p = adjList.vetex[j].firstArc p!=null p = p.next)
{
int k = p.adjvex
if(--adjList.degree[k]==0)
{
S.push(k)
}
if(ve[j]+p.weight>ve[k])
{
ve[k] = ve[j]+p.weight
}
}
}
if(count<adjList.vetexValue.length)
{
System.out.println("图中存在环路!")
return
}
}
public void print()
{
while(!T.isEmpty())
{
System.out.print(T.pop()+" ")
}
}
public void printVel()
{
System.out.println()
for(int i=0i<ve.lengthi++)
{
System.out.print(ve[i]+" ")
}
System.out.println()
for(int i=0i<vl.lengthi++)
{
System.out.print(vl[i]+" ")
}
}
}
转自:http://blog.csdn.net/pigli/article/details/5777048
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)