0
点赞
收藏
分享

微信扫一扫

7-11 关键活动(拓扑排序+逆拓扑排序)

i奇异 2022-03-25 阅读 65
  • 题目链接:7-11 关键活动
  • 考查知识:拓扑排序+逆拓扑排序
  • 题意描述:
    • 关键路径:从源点到汇点的所有路径中,具有最大路径长度的路径称为关键路径,其上的活动称为关键活动
    • n个点,m条有向边,求有向图中的关键路径长度,并按以下要求打印关键路径
    • 关键活动输出的顺序规则是:任务开始的交接点编号小者优先,起点编号相同时,与输入时任务的顺序相反。
  • 思路简析:
    • 从原点出发,按拓扑排序求事件最早发生时间ve
    • 从汇点出发,按逆拓扑排序求事件最迟发生时间vl
    • 根据各个顶点的ve,vl数组求弧的最早e,最迟l开始时间
    • 求AOE网中所有活动的差额d(),找到所有d()=l()-e()=0的活动构成关键活动
  • 具体代码
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N=2e2;
    int in[N],ve[N],vl[N];//in为点的入度,ve为事件最早开始时间,vl为事件最迟开始时间
    vector<pair<int,int> >e[N];//pair为后继结点,与后继结点的边权  
    stack<int>toporder;//按拓扑序列入栈,出栈即逆拓扑序列 
    bool topologicalsort(int n){//拓扑排序,同时求Ve数组
    	queue<int>q;
    	for(int i=1;i<=n;i++){//遍历所有结点,令入度为零的结点入队 
    		if(!in[i])q.push(i);
    	}
    	while(!q.empty()){
    		int u=q.front();
    		q.pop();toporder.push(u);
    		for(int i=0;i<e[u].size();i++){//遍历u所有后继结点
    			int v=e[u][i].first,w=e[u][i].second;//u的i号后继节点编号为v,边权为w 
    			if(--in[v]==0)q.push(v);
    			if(ve[u]+w>ve[v])ve[v]=ve[u]+w;//用ve[u]来跟新u的所有后继结点v
    		}
    	}
    	if(toporder.size()==n)return true;
    	else return false;
    } 
    int criticalpath(int n){
    	memset(ve,0,sizeof(ve));//ve数组初始化
    	if(!topologicalsort(n)){
    		cout<<0;return 0;//不是有向无环图,打印0
    	} 
    	int mx=-1;//关键路径长度 
    	for(int i=1;i<=n;i++){
    		if(ve[i]>mx)mx=ve[i];
    	}cout<<mx<<endl;
    	fill(vl+1,vl+n+1,mx);//vl数组初始化 
    	while(!toporder.empty()){
    		int u=toporder.top();//按拓扑序列入栈,出栈即逆拓扑序列
    		toporder.pop();
    		for(int i=0;i<e[u].size();i++){ 
    			int v=e[u][i].first,w=e[u][i].second;//u的i号后继节点编号为v,边权为w
    			if(vl[v]-w<vl[u])vl[u]=vl[v]-w;//同时向前更新vl数组 
    		}
    	} 
    	for(int i=1;i<=n;i++){//任务开始的交接点编号小者优先
    		for(int j=e[i].size()-1;j>=0;j--){//起点编号相同时,与输入时任务的顺序相反。
    			int v=e[i][j].first,w=e[i][j].second;//i的j号后继节点编号为v,边权为w
    			int e=ve[i],l=vl[v]-w;//遍历领接表的所有边,计算活动最早开始时间e与最晚开始时间l
    			if(e==l)cout<<i<<"->"<<v<<endl; 
    		} 
    	}
    }
    int main(){
    	int n,m,x,y,z;
    	cin>>n>>m;//n个点,m条有向边 
    	while(m--){
    		cin>>x>>y>>z;//x到y的边权为z的有向边 
    		in[y]++;
    		e[x].push_back({y,z});
    	}
    	criticalpath(n);
    	return 0;
    }
    
    
举报

相关推荐

0 条评论