数据结构 图(Part III)

先来写一些概念:

AOV网:顶点表示活动,弧表示优先关系,不存在环,为DAG;

DAG:有向无环图;

如何判断一个有向图是否存在环(是否为DAG图)?

可以用dfs,如果深搜的过程中,搜索到了已经搜过的,那么就说明有环,不是DAG;

也可以用拓扑排序:

1. 拓扑排序

思路就是先建立邻接表,然后判断:如果该点入度为0则入队,并且该点所指向的点的入度减1,如果所指向的点的入度为0那么也入队,重复这个过程

struct EDGE{
	int to,next;
}edge[MAXN];
int in[MAXN],head[MAXN],cnt=0;
void add_edge(int u,int v){
	cnt++;
	in[v]++;   //表示入度++ 
	edge[cnt].to=v,edge[cnt].next=head[u],head[u]=cnt;
}
void topo(){
	queue<int> Q;
	for(int i=1;i<=n;i++){		//n为顶点数 
		if(!in[i]) Q.push(i); 
	} 
	while(!Q.empty()){
		int x=Q.front();
        cout<<x<<" ";
		Q.pop();
		for(int i=head[x];i;i=edge[i].next){
			int v=edge[i].to;
			in[v]--;
			if(!in[v]) Q.push(v); 
		}
	} 
}

以上代码为输出拓扑序的代码,如果该输出没有包含所有的顶点,则说明该图存在环。


再写一些概念:

AOE网:活动在边上,边的权代表活动持续的时间,顶点v表示事件(事件表示在它之前的活动完成),同样也是DAG,不存在环。

缩短哪些活动,可以缩短整个工序的时间呢?

这些活动则被称为关键活动,而关键活动组成了关键路径。

下面再给出如何计算出关键活动:

ps:这公式也太糊了吧,我服了

v_{e}(1)=0; \ \ \ \ \ \ \ v_{l}(n)=v_{e}(n)   n为事件的个数,也指最后一个事件(该顶点出度为0)

事件最早开始的时间:v_{e}(j) = max\left \{ v_{e}(i) + w_{i,j} \right \} 从前往后算,取最大值

事件最晚开始的时间:v_{l}(i)=min\left \{ v_{l}(j)-w_{i,j} \right \} 从后往前推,取最小值

活动最早开始的时间:e(a_{k})=v_{e}(i)     直接等于前面的事件最早开始时间

活动最晚开始的时间:l(a_{k})=v_{l}(j)-w_{i,j}

如果活动最早开始时间=活动最晚开始的时间,那么该活动就为关键活动。当然还有一个条件是最长的,关键路径不能有多条。

 以上都是为了求关键路径服务的,以下为关键路径的代码:

2. 关键路径

因为事件最晚开始的时间是从后往前推的,所以我们需要建立一个逆邻接表,即反向建边就可以啦。然后分别用两次拓扑排序,拓扑排序的同时将min_val和max_val,即事件的最早、最晚开始时间计算出来

输入:

第一行:图的顶点总数,边的总数

第二行开始:每条边的时间长度,格式为源结点   目的结点   长度

输出

第一行:第个顶点的最早开始时间

第二行:每个顶点的最迟开始时间

代码如下:

#include <iostream>
#include <queue>
#define MAX 201
using namespace std;
int n,m;
struct EDGE{
	int to,next,w;
}edge[MAX],inv_edge[MAX];
int cnt=0,head[MAX],in[MAX],min_val[MAX];
int inv_cnt=0,inv_head[MAX],out[MAX],max_val[MAX];
void add_edge(int u,int v,int w){
	cnt++;
    edge[cnt].to=v,edge[cnt].w=w,edge[cnt].next=head[u],head[u]=cnt;
    in[v]++;
    inv_cnt++;
    inv_edge[cnt].to=u,inv_edge[cnt].w=w,inv_edge[cnt].next=inv_head[v],inv_head[v]=inv_cnt;
    out[u]++;
}
void topo(){
	queue<int> Q;
	for(int i=1;i<=n;i++){
		min_val[i]=-1;
		if(!in[i]) Q.push(i),min_val[i]=0;
	}
	while(!Q.empty()){
		int x=Q.front();
		Q.pop();
		for(int i=head[x];i;i=edge[i].next){
			int v=edge[i].to;
			in[v]--;
			min_val[v]=max(min_val[v],min_val[x]+edge[i].w);
			if(!in[v]) Q.push(v); 
		}
	}
}
void inv_topo(){
	queue<int> P;
	for(int i=1;i<=n;i++) {
		max_val[i]=1e9;
	   	if(!out[i]) P.push(i),max_val[i]=min_val[i]; 
	}
	while(!P.empty()){
		int x=P.front();
		P.pop();
		for(int i=inv_head[x];i;i=inv_edge[i].next){
			int v=inv_edge[i].to;
			out[v]--;
			max_val[v]=min(max_val[v],max_val[x]-edge[i].w);
			if(!out[v]) P.push(v);
		}
	}
}
int main(){
	cin>>n>>m;
	for(int i=0;i<m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		u++,v++;
		add_edge(u,v,w);
	}
	topo();
	inv_topo();
	for(int i=1;i<=n;i++) cout<<min_val[i]<<" ";
	cout<<endl;
	for(int i=1;i<=n;i++) cout<<max_val[i]<<" ";
	cout<<endl;
	return 0;
}

ps: 感谢npy的指导,不然不会TAT

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值