拓扑排序

拓扑排序

。_饼干妹妹 提交于 2019-11-26 21:03:34
一、实际生活中的问题   在日常生活中,一项大的工程可以看作是由若干个子工程组成的集合,这些子工程之间必定存在一些先后关系,即某些子工程必须在其它一些子工程完成之后才能开始。   我们可以用有向图来表示这些子工程之间的先后关系:子工程为顶点,子工程之间的先后关系为有向边,这种有向图称为“顶点活动网络”,又称“AOV 网”。一个AOV 网应该是一个有向无环图( Directed Acyclic Graph,DAG ),否则必定会有一些活动互相牵制,造成环中的活动都无法进行。 二、WHAT IS 拓扑排序   在AOV 网中,所有活动可排列成一个线性序列,使得每个活动的所有前驱活动都排在该活动的前面。   对一个DAG G = (V, E) 进行拓扑排序,是将G 中所有顶点排成一个线性序列,使得图中任意一对顶点u 和v,若边u —> v ∈ E,则u 在线性序列中出现在v 之前。   由某个集合上的一个偏序得到该集合上的一个全序,这个操作称为拓扑排序。所得的线性序列,称为拓扑序。 三、算法流程   1. 在有向图中选一个没有前驱的顶点并且输出;   2. 从图中删除该顶点和所有它指出的有向边;   3. 重复上述两步,直至所有顶点输出,或者当前图中不存在无前驱的顶点为止,后者代表我们的有向图是有环的。因此,也可以通过拓扑排序来判断一个图是否有环。 四、例题与代码实现 模板题1 比赛

轰炸行动:tarjan,拓扑排序

二次信任 提交于 2019-11-26 19:41:23
考场上看错题,没什么好说的。 然而它就是一个大板子。 发的题解勉强还能看。但是我还想再讲讲。 题目的表述是,如果从A能直接或间接到B,那么就不能同时轰炸A和B。 那么我们从图里随便拽出一条有向路径,从这条路径中随意挑2个点AB,那么要么能从A到B要么从B到A 那么你任意挑出的这两个点 只要不是同一个点 那么就不能同时轰炸。 带下划线的那一段有什么用呢?它的正确性是显然的。 我所说的“有向路径”没有加任何附加限制,所以可以包括环,环上转几圈就可能出现同一个点呗。 我们考虑单纯的一个环。那么它上的每一个都要单独炸一次。 再考虑单纯的一条路径,那么路径上的每一个点也需要单独炸一次。 如果一个路径进入了一个环,那么这上面的点也必须单独炸一次(路径上的点可以到达环上的任意点)。 如果一个环引出了一个路径,那么环上的点亦可到路径上,都要单独炸一次。 综上,就是要找出一条路径使它上面的不同的点尽量多,不同的点的个数即为答案。 上面那一堆话里已经包括了这个意思:环上的每个点都会给路径长度增加1,且对联通性没有影响。 所以考虑tarjan缩完强联通分量后就没有环了,只不过环变成了权值等于环中点数的一个大点而已 其余普通点的权值为1。现在的问题就变成了在一个DAG里找一条路径使它上面的点权和最大。 不能dfs,因为这是有向的DAG,虽然不是环但也不是树,它可以长得很恶心。

拓扑排序 ---- HDU 1285

夙愿已清 提交于 2019-11-26 17:31:46
代码如下; #include<iostream> #include<vector> #include<queue> #include<cstring> using namespace std; const int maxn = 1000; int in[maxn]; vector<int>V[maxn]; int main() { int n,m,x,y; while(cin>>n>>m){ memset(in,0,sizeof(in)); for(int i=1;i<=n;i++) V[i].clear(); while(m--) { cin>>x>>y; V[x].push_back(y);//构图 in[y]++;// 存入度 } priority_queue<int,vector<int>,greater<int> > q;// 优先数列,小的先出 for(int i=1;i<=n;i++) { if(in[i]==0) { q.push(i); } } int flag=1; while(!q.empty()) { int xx=q.top();q.pop(); if(flag) { cout<<xx; flag=0; } else cout<<" "<<xx; for(int i=0;i<(int)V[xx].size();i++) { in[V[xx][i]]--;

DP&图论 DAY 4 下午图论

空扰寡人 提交于 2019-11-26 17:22:58
DP&图论 DAY 4 下午 后天考试不考二分图,双联通 考拓扑排序 图论 图的基本模型 边: 有向边构成有向图 无向边构成无向图 权值: 1.无权 2.点权 3.边权 4.负权(dij不可以跑) 环: 1. 2.重边 3.有向无环图DAG 路径: 1.简单路径:不经过重复的点 1-->2-->3 不简单路径:经过重复点 1-->2-->3-->1-->4 2.连通,具有传递性 图: 1.树:n个点,n-1条边的无环连通图 2.完全图:一个无向图,图中任意两点之间都有一条连边 3.竞赛图:完全图中的每一条边确定一个方向 4.基环树 5.仙人掌:不是树,图里可以成环,每一条边,要么不在环上,要么只属于一个环 图的输入方式 图的存储方式 图的遍历方法 拓扑排序基于BFS 三种遍历顺序 1.前序遍历 中左右 2.中序遍历 左中右 3.后序遍历 左右中 QUS:给前序中序,写后序 前序第一个就是根,找到在中序的位置,递归 例题1: 给定一个有向图,边权为 1 或 2 ,求单源最短路。 >Solution 稍微改写一下 BFS 即可。 # 创建三个集合, Q 0 表示当前层, Q 1 表示距离为 1 的层, Q 2 表示距离为 2 的层,初始 Q 0 = { s } , Q 1= ∅ , Q 2= ∅ # 依次取出 Q 0 中的点,将其邻点放入对应的 Q 1 或 Q 2 中 # Q 0 =

乱七八糟的图论1

老子叫甜甜 提交于 2019-11-26 17:19:53
图的基本概念 图是点和边组成的集合体,G=<V,E>; V是点集 E是边集 有向边,有向图 无向边,无向图 无权、点权、边权、负权 环、自环、重边、有向无环图(DAG) 路径、简单路径:没有经过重复的点、连通 树:n个点n-1条边的连通图、完全图:任何两点间都有一条边(无向图)、竞赛图:将完全图上的每条边定一个方向、基环树:有一个环,其他全是树、仙人掌:可以存在环,但是每一条边,至多只存在于一个环中 图的输入方式 最常见的输入方式是用 1 - N 表示顶点,并逐行给出 M 条边所连接的两点和边权大小。 N M u1 v1 w1 u2 v2 w2 . . . um vm wm 如果有点权,一般会用一行 N 个数表示每个点的权值大小。 p1 p2 . . . pn 邻接矩阵是最简单的图存储方式。 对于 N 个点的图,用 N × N 的二维数组记录两点之间是否有边相连,以及边权大小。 空间复杂度 O(N^2) 对于边不存在的情况,可能采用 ∞,也可能使用 0 或 -1 等等特殊值,视具体情况而定。 对于有重边的情况,可能不能完整保留所有边的信息。 邻接表是更为常用的图存储方式。 对每个点 u 用不定长度数组或链表存储所有以其为起点形如< u, v > 的边。 无向图双向边拆分为两条单向边。 不定长数组 链表 空间复杂度 O(N + M) 图的遍历方法: 广度优先搜索bfs 队列

hdu1285 确定比赛名次(拓扑排序)

瘦欲@ 提交于 2019-11-26 10:25:05
确定比赛名次 有N个比赛队(1<=N<=500),编号依次为1,2,3,。。。。,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委员会不能直接获得每个队的比赛成绩,只知道每场比赛的结果,即P1赢P2,用P1,P2表示,排名时P1在P2之前。现在请你编程序确定排名。 Input 输入有若干组,每组中的第一行为二个数N(1<=N<=500),M;其中N表示队伍的个数,M表示接着有M行的输入数据。接下来的M行数据中,每行也有两个整数P1,P2表示即P1队赢了P2队。 Output 给出一个符合要求的排名。输出时队伍号之间有空格,最后一名后面没有空格。 其他说明:符合条件的排名可能不是唯一的,此时要求输出时编号小的队伍在前;输入数据保证是正确的,即输入数据确保一定能有一个符合要求的排名。 Sample Input 4 3 1 2 2 3 4 3 Sample Output 1 2 4 3 ac代码: #include<iostream> #include<algorithm> #include<queue> #include<vector> using namespace std; int in[600]; vector <int> ve[600]; int main(){ int n,m; while(cin>>n>>m){ for(int i=1;i<

拓扑排序 +队列基础知识+不定数组+例题

戏子无情 提交于 2019-11-26 10:12:32
1、什么是拓扑排序 对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图 中任 意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序 (Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操 作称之为拓扑排序。 无向图和有环的有向图没有拓扑排序拓扑排序其实就是离散上的偏序关系的一个应用 2、拓扑排序的步骤: 1.按照一定的顺序进行构造有向图,记录后个节点的入度; 2,从图中选择一个入度为0的顶点,输出该顶点; 3,从图中 删除该顶点及所有与该顶点相连的边 4,重复上述两步,直至所有顶点输出。 5或者当前图中不存在入度为0的顶点为 止。此时可说明图中有环。 6,因此,也可以通过拓扑排序来判断一个图是否有环。 拓扑排序模版 #include <bits/stdc++.h> using namespace std; int str[10000];//存入度 vector<int> v[10000];//存关系 构建图 int main() { int n,m; int x,y; while(cin>>n>>m)// 根据题目要求可以改动 { memset(str,0,sizeof(str)

HDU6165 Tarjan缩点+拓扑排序

左心房为你撑大大i 提交于 2019-11-26 09:57:58
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=6165 题意:给出一个有向无自环,无重边的图,判断任意两点是否能到达(只要一个能抵达另一个即可) 这是做的第一道拓扑排序的题目,讲解可见: https://blog.csdn.net/qq_41713256/article/details/80805338 分析:因为只要求两点中任一点可到达另一点,也就是只要求是弱连通图。首先强连通分量内部肯定是可以是任意到达的,我们先利用Tarjan进行缩点形成一个有向无环图(DAG),在这个基础上我们进行拓扑排序,只要存在一条路径连接所有点的话,那么肯定是可以到达了,基于此我们在拓扑排序时,如果每一层都只有一个点入度为0 ,就可以得到唯一的一条路径连接所有点了,也就是说这时候就可以到达了。 #include<bits/stdc++.h> #define maxn 1011 using namespace std; vector<int>G[maxn],new_G[maxn]; stack<int>s; int n,m; int dfn[maxn],vis[maxn],low[maxn],color[maxn],colornum,cnt,in[maxn]; //in数组记录入度 bool toposort(int n){ queue<int>q;

拓扑排序 模板+图的两种表示方式

Deadly 提交于 2019-11-25 23:42:04
https://www.cnblogs.com/1242118789lr/p/6740930.html 可以用邻接表也可以用邻接矩阵 当数据量级很大的时候我们选择邻接表,数据量小的时候可以选择邻接表也可以选择邻接矩阵 如UVA10305  John has n tasks to do. Unfortunately, the tasks are not independent and the execution of one task is only possible if other tasks have already been executed. Input The input will consist of several instances of the problem. Each instance begins with a line containing two integers, 1 <= n <= 100 and m . n is the number of tasks (numbered from 1 to n ) and m is the number of direct precedence relations between tasks. After this, there will be m lines with two integers i and j