PTA:最短工期 (25分)(拓扑排序笔记--入门到解题)

拓扑排序

转载自: https://blog.csdn.net/y_universe/article/details/79342940
定义
对一个有向无环图(Directed Acyclic Graph简称DAG) G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
  在AOV网中,若不存在回路,则所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面,我们把此序列叫做拓扑序列(Topological order),由AOV网构造拓扑序列的过程叫做拓扑排序(Topological sort)。AOV网的拓扑序列不是唯一的,满足上述定义的任一线性序列都称作它的拓扑序列。
换句话说: 在实践的解题中,多用于求解具有严格顺序要求的活动及这个活动所耗费的时间。如要完成B活动,首先要完成A活动才行,也就是说只有A活动完成了才能做B活动。具体见后面例题解析。

拓扑排序的实现步骤
1. 从图中选择一个没有前驱(即入度为0)的顶点并输出。
2. 删除该顶点和所有以它为起点的有向边。
重复 1 和 2 直到当前的图为空或当前图中不存在无前驱的顶点为止。若当前图中不存在无前驱的顶点说明有向图中必存在环。(ps:存在环的情况也就是题目中无解的情况)

例题1:最短工期 (25分)

在这里插入图片描述
输入样例 1:

9 12
0 1 6
0 2 4
0 3 5
1 4 1
2 4 1
3 5 2
5 4 0
4 6 9
4 7 7
5 7 4
6 8 2
7 8 4

输出样例 1:

18

输入样例 2:

4 5
0 1 1
0 2 2
2 1 3
1 3 4
3 2 5

输出样例 2:

Impossible

解析:
就如题目所说的,一个项目的任务也是有先后依赖关系的,也就是说是有严格的顺序执行的。这里就可以用到拓扑排序,只是这里不是求任务完成顺序,而是最早完工时间。
具体解析见AC代码:

#include<bits/stdc++.h>
using namespace std;
int dis[105][105],ru[105],f[105];
int n,ans,cnt=0;

void topological_sort(){
	while(1){
		int flag=0;
		for(int i=0;i<n;i++){
    		if(!ru[i]){  //入度为0的顶点(即没有前驱任务的任务,那么这个任务就可以做了) 
    			ru[i]--;//变为-1,也就是这个任务做完了,后面不用考虑了 
    			cnt++;
    			flag=1;
    			for(int j=0; j<n; j++){
    				if(dis[i][j]!=-1){//存在以i为前驱任务的任务 
                    	ru[j]--; // 入度减一(把i任务去掉了,则j的前驱任务少了一个) 
                    	f[j]=max(f[j],f[i]+dis[i][j]); //选择最长的工作时间 
                   		ans=max(ans,f[j]);//统计最早完工时间 
                	}	
				}
			}
		}
		if(!flag) break;  //不存在入度为0的点,跳出循环 	
	}
	if(cnt==n) cout<<ans<<endl; //全部顶点和边成功去掉 
	else cout<<"Impossible"<<endl;//存在环 
}

int main(){
	int m, p, x, y;
	cin>>n>>m;
	for(int i=0; i<n; i++)
		for(int j=0; j<n; j++)
			dis[i][j]=-1;//初始边的权都为-1,因为工作时长可能是0,所以初始dis都赋值为-1 
	while(m--){
		cin>>x>>y>>p;
		dis[x][y]=p;
		ru[y]++;//ru[y]顶点y的入度,(即题目中的y任务的前驱任务们) 
	} 
	topological_sort();    //拓扑排序
	return 0;
}

优化:
用vector,queue优化的算法,多用于只求活动执行的顺序,不需要求权值

void topological_sort(){ 
	queue<int>q;
    vector<int>edge[n];
    for(int i=0;i<n;i++)  //n:结点的总数
        if(in[i]==0) q.push(i);  //将入度为0的点入队列
    vector<int>ans;   //ans 为拓扑序列
    while(!q.empty())
    {
        int p=q.front(); q.pop(); // 选一个入度为0的点,出队列
        ans.push_back(p);
        for(int i=0;i<edge[p].size();i++)
        {
            int y=edge[p][i];
            in[y]--;
            if(in[y]==0) q.push(y);  
        }
    }
    if(ans.size()==n){
        for(int i=0;i<ans.size();i++)
            cout<<ans[i]<<" "; 
    }else cout<<"No Answer!\n";   //  ans中的长度与n不相等,存在环 
}

推荐题目:https://acm.zcmu.edu.cn/JudgeOnline/problem.php?id=2153
推荐博客:https://blog.csdn.net/qq_41713256/article/details/80805338
欢迎大家批评改正!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章