AOV网为有向图,用其表示一项工程,则顶点表示活动,弧表示活动之间的优先关系。此时AOV网中不能出现回路,否则会出现某活动开始是以自身为先决条件这样的问题。拓扑排序就是用以测试AOV网是否存在回路的方法。拓扑排序适合图的邻接表存储形式。
拓扑序列:对有向图G=(V,E),V中顶点序列v0,v1,…,vn-1为拓扑序列,当且仅当满足:若从顶点vi到vj存在一条路径,则在顶点序列中vi一定在vj之前。
/*边表结点*/
struct ArcNode
{
int adjvex;
ArcNode* next;
};
/*顶点表结点
按需要在顶点表加入某顶点的入度*/
template<class DataType>
struct VertexNode
{
DataType vertex;
int in;
ArcNode* first;
};
/*邻接表*/
template <class DataType>
class ALGraph
{
friend void TopSort(ALGraph<DataType>&);
public:
ALGraph(std::vector<DataType>& a, int n)
{
vertex_num = n;
for (int i = 0; i < vertex_num; ++i)
{
VertexNode<DataType> temp;
temp.vertex = a[i];
temp.first = NULL;
temp.in = 0;
adjlist.push_back(temp);
}
int i, j;
int l;
while (cin >> i >> j >> l)
{
if (i == -1)
break;
ArcNode *s;
s = new ArcNode;
s->adjvex = j;
s->next = adjlist[i].first;
adjlist[i].first = s;
++adjlist[j].in;
}
}
~ALGraph()
{
for (int i=0; i < vertex_num; ++i)
{
ArcNode* q;
while (adjlist[i].first != NULL)
{
q = adjlist[i].first;
adjlist[i].first = adjlist[i].first->next;
delete q;
}
}
}
private:
std::vector<VertexNode<DataType>> adjlist;//顶点表
size_t vertex_num;
};
/*拓扑排序
1.将所有入度为0的点入栈
2.栈顶元素i出栈,输出i
3.对i的每一个邻接点k的入度-1;若k的入度为0,入栈
4.重复2,3直到所有点被遍历*/
void TopSort(ALGraph<int>& G)
{
list<int> S;//栈S
int count = 0;
for (size_t i = 0; i < G.vertex_num; ++i)
if (G.adjlist[i].in == 0)
S.push_front(i);
while (S.begin()!=S.end())
{
int i = *(S.begin());
S.pop_front();
cout << G.adjlist[i].vertex << endl;
++count;
ArcNode* p = G.adjlist[i].first;
while (p != NULL)
{
int j = p->adjvex;
--G.adjlist[j].in;
if (G.adjlist[j].in == 0)
S.push_front(j);
p = p->next;
}
}
if (count < G.vertex_num)
cout << "有回路" << endl;
}
与AOV网不同,AOE网的顶点表示事件,有向边表示活动,其权值表示活动持续时间,没有入边的为起点,没有出边的为终点。与AOV网相同的是其有向边代表了事件的优先顺序以及事件与活动的关系。因为某些活动能同时进行,从起点到终点有许多条路径,AOE网的关键路径是其中一条最大路径长度的路径,其路径长度即为工程所用最短工期,路径上的活动为关键活动,关键路径的算法就是求解AOE网中的关键活动。关键路径不能出现回路,即需要满足拓扑排序及逆拓扑排序,同时,关键路径也不唯一,若一个关键活动不在所有关键路径上,改变它将不影响工期。
要求解关键活动,需要求出4个值(1)事件最早发生时间ve[k](2)事件最晚发生时间vl[k](3)活动最早开始时间ee[i](4)活动最晚开始时间el[i];对于一个活动,若它是关键活动,则此活动的最早最晚开始时间应相等ee[i]=el[i],即想改变工期须改变关键活动。 对于活动i <vj,vk>
(1) 源点到vk的最大路径长度。ve[0]=0; ve[k]=max{ve[j]+length<vj,vk>};
(2) vl[n-1]=ve[n-1]; vl[j]=min{vl[k]-length<vj,vk>};
(3) ee[i]=ve[k];
(4) el[i]=vl[k]-length<vj,vk>
步骤:
1. 从源点v0出发按拓扑排序求其余各顶点的ve[],若存在回路,终止
2.从终点vn-1出发按逆拓扑排序求其余各顶点vl[],若存在回路,终止
3.根据各顶点的ve和vl,求各点的ee和el
4.若某条有向边ai满足ee[]=el[],则ai为关键路径
/*边表结点*/
struct ArcNode
{
int ee, el;
int len;
int adjvex;
bool flag;//是否在关键路径上
};
/*顶点表结点*/
template<class DataType>
struct VertexNode
{
DataType vertex;
int ve, vl;
int in,out;
bool is_end;
list<ArcNode> vin, vout;
};
/*邻接表求关键路径*/
template <class DataType>
class ALGraph
{
public:
ALGraph(std::vector<DataType>& a, int n)
{
vertex_num = n;
for (int i = 0; i < vertex_num; ++i)
{
VertexNode<DataType> temp;
temp.vertex = a[i];
temp.in = 0;
temp.out = 0;
temp.ve = 0;
temp.vl = INF;
temp.is_end = false;
adjlist.push_back(temp);
}
int i, j;
int l;
while (cin >> i >> j >> l)
{
if (i == -1)
break;
ArcNode s,t;
s.adjvex = j;
s.len = l;
s.flag = false;
t.adjvex = i;
t.len = l;
s.flag = false;
++adjlist[i].out;
++adjlist[j].in;
adjlist[i].vout.push_back(s);
adjlist[j].vin.push_back(t);
}
}
void CPM()//若某条边ee=el,则为关键活动
{
if (!TopSort())
{
cout << "有回路" << endl;
return;
}
if (!reTopSort())
{
cout << "有回路" << endl;
return;
}
vector<int> sum;
getee();
getel();
int s = 0;
for (int i = 0; i < vertex_num; ++i)
{
list<ArcNode>::iterator it;
for (it = adjlist[i].vout.begin(); it != adjlist[i].vout.end(); ++it)
if (it->flag)
{
s += it->len;
int k = it->adjvex;
if (adjlist[k].is_end)
{
sum.push_back(s);
s = 0;
}
}
}
for (size_t i = 0; i < sum.size(); ++i)
if (s < sum[i])
s = sum[i];
cout << s << endl;
}
private:
std::vector<VertexNode<DataType>> adjlist;//顶点表
int vertex_num;
bool TopSort()//按拓扑排序求最早发生时间ve
{
list<int> S;//栈S
int count = 0;
for (int i = 0; i < vertex_num; ++i)//起点入栈
if (adjlist[i].in == 0)
{
adjlist[i].ve = 0;//计算式ve[0]=0;
S.push_front(i);
}
while (S.begin() != S.end())//栈非空时循环
{
int i = *(S.begin());
S.pop_front();
++count;
list<ArcNode>::iterator it;
for (it = adjlist[i].vout.begin(); it != adjlist[i].vout.end(); ++it)
{
int k = it->adjvex;
if (adjlist[k].ve < (adjlist[i].ve + it->len))//计算式ve[k]=max{ve[j]+len<vj,vk>},j到k
adjlist[k].ve = adjlist[i].ve + it->len;
--adjlist[k].in;
if (adjlist[k].in == 0)
S.push_front(k);
}
}
if (count < vertex_num)
return false;
else
return true;
}
bool reTopSort()//按逆拓扑排序求最迟发生时间vl
{
list<int> S;//栈S
int count = 0;
for (int i = 0; i < vertex_num; ++i)//终点入栈
if (adjlist[i].out == 0)
{
adjlist[i].vl = adjlist[i].ve;//算式vl[n-1]=ve[n-1]
adjlist[i].is_end = true;
S.push_front(i);
}
while (S.begin() != S.end())//栈非空时循环
{
int i = *(S.begin());
S.pop_front();
++count;
list<ArcNode>::iterator it;
for (it = adjlist[i].vin.begin(); it != adjlist[i].vin.end(); ++it)
{
int k = it->adjvex;
if (adjlist[k].vl > (adjlist[i].vl - it->len))//算式vl[k]=max{vl[j]-len<j,k>},j到k
adjlist[k].vl = adjlist[i].vl - it->len;
--adjlist[k].out;
if (adjlist[k].out == 0)
S.push_front(k);
}
}
if (count < vertex_num)
return false;
else
return true;
}
void getee()//由ve求ee
{
for (int i = 0; i < vertex_num; ++i)
{
list<ArcNode>::iterator it;
for (it = adjlist[i].vout.begin(); it != adjlist[i].vout.end(); ++it)
{
it->ee = adjlist[i].ve;
}
}
}
void getel()//由vl求el
{
for (int i = 0; i < vertex_num; ++i)
{
list<ArcNode>::iterator it;
for (it = adjlist[i].vin.begin(); it != adjlist[i].vin.end(); ++it)
{
it->el = adjlist[i].vl - it->len;
int k = it->adjvex;
list<ArcNode>::iterator t;
for (t = adjlist[k].vout.begin(); t != adjlist[k].vout.end(); ++t)
if (t->adjvex = i)
{
t->el = it->el;
if (t->ee == t->el)
t->flag = true;
break;
}
}
}
}
};