一、基本算法

拓扑序列:对于一张有向图,求一个序列ai若对于每一条边(u,v),都满足au<=a,则称这个序列为这张有向图的拓扑序列,一张图可能有多个拓扑序列。

求拓扑序列:找到入度为0的点,加入队列中,每次取出队列顶端的点加入拓扑序列的最后,将它到达的点的入度-1,然后再重复做,直到没有点的入度为0,若最后还是有点的入度大于0,则说明有向图中存在环。

代码:

  1. void add(int x,int y)
  2. {
  3. num++;
  4. in[y]++;
  5. End[num]=y;
  6. Next[num]=Head[x];
  7. Head[x]=num;
  8. }
  9. void topsort()
  10. {
  11. queue<int>q;
  12. for(int i=;i<=n;i++)
  13. if(!in[i])q.push(i);
  14. while(!q.empty())
  15. {
  16. int x=q.front();q.pop();
  17. seq[++cnt]=x;
  18. for(int i=Head[x];i;i=Next[i])
  19. {
  20. int y=End[i];
  21. in[y]--;
  22. if(in[y]==)q.push(y);
  23. }
  24. }
  25. }

二、应用

1.求字典序最小/最大的拓扑序列。

只需将上面代码中的队列换成小根堆/大根堆即可,每次取出堆顶,方法相同。

2.洛谷P1983车站分级

由于存在明显的优先级关系,所以考虑拓扑排序,按照小于的关系连边,然后拓扑排序一遍,找到最大的d[i]就是最少需要分的级别,因为这一段序列中的点必须满足级别严格递减。(再具体的题解可以看cellur的题解)

代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int M=2e6;
  4. int n,m,cnt=,sta[],d[],in[],w[];
  5. bool v[];
  6. int num=,end[M],next[M],head[];
  7. void add(int x,int y)
  8. {
  9. if()puts("AC");
  10. num++;
  11. in[y]++;
  12. end[num]=y;
  13. next[num]=head[x];
  14. head[x]=num;
  15. }
  16. void topsort()
  17. {
  18. queue<int>q;
  19. for(int i=;i<=n+m;i++)
  20. if(!in[i])q.push(i),d[i]=w[i];
  21. while(q.size())
  22. {
  23. int x=q.front();q.pop();
  24. for(int i=head[x];i;i=next[i])
  25. {
  26. int y=end[i];
  27. in[y]--;
  28. d[y]=d[x]+w[y];
  29. if(in[y]==)q.push(y);
  30. }
  31. }
  32. }
  33. int main()
  34. {
  35. cin>>n>>m;
  36. for(int i=;i<=n;i++)
  37. w[i]=;
  38. for(int i=;i<=m;i++)
  39. {
  40. memset(v,,sizeof v);
  41. cin>>cnt;
  42. for(int j=;j<=cnt;j++)
  43. scanf("%d",&sta[j]),v[sta[j]]=;
  44. for(int j=sta[];j<=sta[cnt];j++)
  45. if(!v[j])add(j,n+i);
  46. for(int j=;j<=cnt;j++)
  47. add(n+i,sta[j]);
  48. }
  49. topsort();
  50. int ans=;
  51. for(int i=;i<=n;i++)
  52. ans=max(ans,d[i]);
  53. cout<<ans<<endl;
  54. return ;
  55. }

3.洛谷P3243菜肴

最先想到的就是求出字典序最小的拓扑序列,然而我们很容易举出反例,如两个限制:5>2,4>3,这时的答案显然是1 5 2 4 3,而不是1 4 3 5 2,而后者的字典序小于前者。那么我们不妨换一种思路,在反图中求出字典序最大的序列,然后反着输出,如果遇到环就特判输出Impossible。这样为什么是对的呢?因为求最大的拓扑序的话就是在能放的中选择一个最大的放到最前面,如果不放这个而放次大的能放的,那么反过来之后,这个最大的一定在次大的前面,而本来它是可以放到次大的后面的,所以这样一定是最优的。

至于求字典序最大的拓扑序的方法在前面已经说过,这里用大根堆实现,直接看代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int M=1e5+;
  4. int T,n,m,in[M],seq[M];
  5. int num=,cnt=,Head[M],Next[M],End[M];
  6. void add(int x,int y)
  7. {
  8. num++;
  9. End[num]=y;
  10. Next[num]=Head[x];
  11. Head[x]=num;
  12. }
  13. void clear()
  14. {
  15. cnt=num=;
  16. for(int i=;i<=n;i++)
  17. in[i]=Head[i]=;
  18. for(int i=;i<=m;i++)
  19. Next[i]=End[i]=;
  20. }
  21. bool topsort()
  22. {
  23. priority_queue<int>q;//大根堆
  24. for(int i=n;i>=;i--)
  25. if(!in[i])q.push(i);
  26. while(!q.empty())
  27. {
  28. int x=q.top();q.pop();
  29. seq[++cnt]=x;
  30. for(int i=Head[x];i;i=Next[i])
  31. {
  32. int y=End[i];
  33. in[y]--;
  34. if(in[y]==)q.push(y);
  35. }
  36. }
  37. for(int i=;i<=n;i++)
  38. if(in[i]>)return ;
  39. return ;
  40. }
  41. int main()
  42. {
  43. scanf("%d",&T);
  44. while(T--)
  45. {
  46. scanf("%d%d",&n,&m);
  47. clear();
  48. for(int i=;i<=m;i++)
  49. {
  50. int x,y;
  51. scanf("%d%d",&x,&y);
  52. add(y,x);
  53. in[x]++;
  54. }
  55. if(!topsort())puts("Impossible!");
  56. else{
  57. for(int i=n;i>=;i--)
  58. printf("%d ",seq[i]);
  59. puts("");
  60. }
  61. }
  62. return ;
  63. }

4.HDU5222 Exploration

题意:给你一张混合图(既有有向边又有无向边),问有没有简单环。

先将无向边连接的点通过并查集合并成一个点,同时判断是否只通过无向边就能形成环,然后再加入有向边,注意添加的边的起始点为起始点所在集合编号,终点为终点所在集合编号,同时也要判断添加的起始点是否已经属于同一个集合,然后拓扑排序一遍,判断缩点后的有向图是否存在环,当然也可以用Tarjan做。注意多组数据一定要每次把邻接表也清零,因为这个WA了好多次。。。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<queue>
  4. using namespace std;
  5. const int M=1e6+;
  6. int T,n,m1,m2;
  7. int fa[M],in[M];
  8. int num=,Head[M],Next[M],End[M];
  9. int get(int x)
  10. {
  11. if(x==fa[x])return x;
  12. else return fa[x]=get(fa[x]);
  13. }
  14. void Union(int x,int y)
  15. {
  16. fa[get(x)]=get(y);
  17. }
  18. void add(int x,int y)
  19. {
  20. num++;
  21. End[num]=y;
  22. Next[num]=Head[x];
  23. Head[x]=num;
  24. }
  25. void clear()
  26. {
  27. num=;
  28. for(int i=;i<=n;i++)
  29. fa[i]=i,in[i]=,Head[i]=;
  30. for(int i=;i<=m2;i++)
  31. End[i]=Next[i]=;
  32. }
  33. bool topsort()
  34. {
  35. queue<int>q;
  36. for(int i=;i<=n;i++)
  37. if(fa[i]==i&&in[i]==)q.push(i);
  38. while(q.size())
  39. {
  40. int x=q.front();q.pop();
  41. for(int i=Head[x];i;i=Next[i])
  42. {
  43. int y=End[i];
  44. in[y]--;
  45. if(in[y]==)q.push(y);
  46. }
  47. }
  48. for(int i=;i<=n;i++)
  49. if(fa[i]==i&&in[i]>)return ;
  50. return ;
  51. }
  52. int main()
  53. {
  54. scanf("%d",&T);
  55. while(T--)
  56. {
  57. bool flag=;
  58. int x,y;
  59. scanf("%d%d%d",&n,&m1,&m2);
  60. clear();
  61. for(int i=;i<=m1;i++)//加无向边
  62. {
  63. scanf("%d%d",&x,&y);
  64. if(get(x)==get(y))flag=;
  65. //如果无向边就能构成环
  66. else Union(x,y);
  67. }
  68. for(int i=;i<=m2;i++)
  69. {
  70. scanf("%d%d",&x,&y);
  71. int fx=get(x),fy=get(y);
  72. if(fx==fy)flag=;
  73. add(fx,fy);
  74. in[fy]++;
  75. }
  76. if(flag)puts("YES");
  77. else{
  78. bool f=topsort();
  79. if(f)puts("YES");
  80. else puts("NO");
  81. }
  82. }
  83. return ;
  84. }

拓扑排序复习——Chemist的更多相关文章

  1. 《数据结构与算法分析:C语言描述》复习——第九章“图论”——拓扑排序

    2014.07.04 17:23 简介: 我们考虑一种特殊的图: 1. 有向图 2. 只有一个连通分量 3. 不存在环 那么这样的图里,必然可以找到一种排序方式,来确定谁在谁的“前面”. 简单的来说可 ...

  2. noip复习之拓扑排序

    之前很多很多紫书上的东西我都忘了…… 抄题解的后果…… 做了一下裸题 https://vjudge.net/problem/UVA-10305 拓扑排序还可以来判环 #include<bits/ ...

  3. 关于最小生成树,拓扑排序、强连通分量、割点、2-SAT的一点笔记

    关于最小生成树,拓扑排序.强连通分量.割点.2-SAT的一点笔记 前言:近期在复习这些东西,就xjb写一点吧.当然以前也写过,但这次偏重不太一样 MST 最小瓶颈路:u到v最大权值最小的路径.在最小生 ...

  4. Poj 2367 Genealogical tree(拓扑排序)

    题目:火星人的血缘关系,简单拓扑排序.很久没用邻接表了,这里复习一下. import java.util.Scanner; class edge { int val; edge next; } pub ...

  5. 2016"百度之星" - 初赛(Astar Round2A)HDU 5695 拓扑排序+优先队列

    Gym Class Time Limit: 6000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total S ...

  6. Luogu P1113 杂务 【拓扑排序】 By cellur925

    题目传送门 这题我们一看就知道是拓扑排序,然而在如何转化问题上花了大工夫,一个小时后最后还是无奈看了题解qwq. 显然我们可以对于每个任务,从他的前导任务到他连一条边,最后我们可以得到一个DAG.在这 ...

  7. Codeforces Round #460 (Div. 2)_D. Substring_[dp][拓扑排序]

    题意:一个有向图,每个结点 被赋予一个小写字母,一条路径的value等与这条路径上出现次数最多的字母的数目,求该图的最大value 比赛时,用dfs超时,看官方题解用的dp和拓扑排序,a--z用0-2 ...

  8. DAG及拓扑排序

    1.有向无环图和拓扑排序 有向无环图(Directed Acyclic Graph,简称DAG):拓扑排序指的对DAG一个有序的线性排列.即每次选出一个没有入度的节点,然后输出该点并将节点和其相关连的 ...

  9. HDU-2647 Reward(链式前向星+拓扑排序)

    Reward Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submis ...

随机推荐

  1. MTK 手机芯片 2014 Roadmap

    注:其中 A53 架构的是 64 位处理器. MT6733 魅蓝 MT6752 魅族 note 联通/移动版 MT6595 魅族 MX4

  2. 项目Beta冲刺(团队5/7)

    项目Beta冲刺(团队5/7) 团队名称: 云打印 作业要求: 项目Beta冲刺(团队) 作业目标: 完成项目Beta版本 团队队员 队员学号 队员姓名 个人博客地址 备注 221600412 陈宇 ...

  3. HDU 6109 数据分割 【并查集+set】 (2017"百度之星"程序设计大赛 - 初赛(A))

    数据分割 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  4. goroutine pool,WaitGroup,chan 示例

    服务端高并发编程经常需要写很多goroutine来服务每一个连接,如何正确使用goroutine池是又拍云的工程师们需要考虑的问题,今天这篇文章,分享给同样需要使用go语言的小伙伴们. 文/陶克路 本 ...

  5. Java 深拷贝浅拷贝 与 序列化

    一.浅拷贝.深拷贝 浅拷贝会对对象中的成员变量进行拷贝:如果是基本类型,拷贝的就是基本类型的值:如果属性是内存地址(引用类型),拷贝的就是内存地址 : 深拷贝,除了基本类型外,引用类型所引用的对象也会 ...

  6. centos 网络连接查看

    安装iftop. 这个得先安装epel #yum install epel-replease -y #yum install iftop -y #iftop

  7. js 中继承的几种方式

    继承的方式一共有三种: 一.原型继承 通过prototype   来实现继承. function Person(name,age) { this.name=name; this.age=age; } ...

  8. bzoj3137: [Baltic2013]tracks

    炸一看好像很神仙的样子,其实就是个sb题 万年不见的1A 但是我们可以反过来想,先选一个起点到终点的联通块,然后这联通块后面相当于就能够走了,继续找联通块 然后就能发现直接相邻的脚步相同的边权为0,否 ...

  9. POJ1860 Currency Exchange —— spfa求正环

    题目链接:http://poj.org/problem?id=1860 Currency Exchange Time Limit: 1000MS   Memory Limit: 30000K Tota ...

  10. 织梦系统如何设置URL绝对路径及绝对路径的好处

    今天,和大家分享下织梦系统如何设置URL绝对路径及绝对路径的好处,我的一些就是用的织梦系统,感觉织梦在SEO优化方面做的还是非常好的,至少在CMS系统中应该是做的最出色的吧!下面,我就先来讲下这个织梦 ...