一、基本算法

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

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

代码:

void add(int x,int y)
{
num++;
in[y]++;
End[num]=y;
Next[num]=Head[x];
Head[x]=num;
}
void topsort()
{
queue<int>q;
for(int i=;i<=n;i++)
if(!in[i])q.push(i);
while(!q.empty())
{
int x=q.front();q.pop();
seq[++cnt]=x;
for(int i=Head[x];i;i=Next[i])
{
int y=End[i];
in[y]--;
if(in[y]==)q.push(y);
}
}
}

二、应用

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

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

2.洛谷P1983车站分级

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

代码:

 #include<bits/stdc++.h>
using namespace std;
const int M=2e6;
int n,m,cnt=,sta[],d[],in[],w[];
bool v[];
int num=,end[M],next[M],head[];
void add(int x,int y)
{
if()puts("AC");
num++;
in[y]++;
end[num]=y;
next[num]=head[x];
head[x]=num;
}
void topsort()
{
queue<int>q;
for(int i=;i<=n+m;i++)
if(!in[i])q.push(i),d[i]=w[i];
while(q.size())
{
int x=q.front();q.pop();
for(int i=head[x];i;i=next[i])
{
int y=end[i];
in[y]--;
d[y]=d[x]+w[y];
if(in[y]==)q.push(y);
}
}
}
int main()
{
cin>>n>>m;
for(int i=;i<=n;i++)
w[i]=;
for(int i=;i<=m;i++)
{
memset(v,,sizeof v);
cin>>cnt;
for(int j=;j<=cnt;j++)
scanf("%d",&sta[j]),v[sta[j]]=;
for(int j=sta[];j<=sta[cnt];j++)
if(!v[j])add(j,n+i);
for(int j=;j<=cnt;j++)
add(n+i,sta[j]);
}
topsort();
int ans=;
for(int i=;i<=n;i++)
ans=max(ans,d[i]);
cout<<ans<<endl;
return ;
}

3.洛谷P3243菜肴

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

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

 #include<bits/stdc++.h>
using namespace std;
const int M=1e5+;
int T,n,m,in[M],seq[M];
int num=,cnt=,Head[M],Next[M],End[M];
void add(int x,int y)
{
num++;
End[num]=y;
Next[num]=Head[x];
Head[x]=num;
}
void clear()
{
cnt=num=;
for(int i=;i<=n;i++)
in[i]=Head[i]=;
for(int i=;i<=m;i++)
Next[i]=End[i]=;
}
bool topsort()
{
priority_queue<int>q;//大根堆
for(int i=n;i>=;i--)
if(!in[i])q.push(i);
while(!q.empty())
{
int x=q.top();q.pop();
seq[++cnt]=x;
for(int i=Head[x];i;i=Next[i])
{
int y=End[i];
in[y]--;
if(in[y]==)q.push(y);
}
}
for(int i=;i<=n;i++)
if(in[i]>)return ;
return ;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
clear();
for(int i=;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(y,x);
in[x]++;
}
if(!topsort())puts("Impossible!");
else{
for(int i=n;i>=;i--)
printf("%d ",seq[i]);
puts("");
}
}
return ;
}

4.HDU5222 Exploration

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

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

代码:

 #include<iostream>
#include<cstdio>
#include<queue>
using namespace std;
const int M=1e6+;
int T,n,m1,m2;
int fa[M],in[M];
int num=,Head[M],Next[M],End[M];
int get(int x)
{
if(x==fa[x])return x;
else return fa[x]=get(fa[x]);
}
void Union(int x,int y)
{
fa[get(x)]=get(y);
}
void add(int x,int y)
{
num++;
End[num]=y;
Next[num]=Head[x];
Head[x]=num;
}
void clear()
{
num=;
for(int i=;i<=n;i++)
fa[i]=i,in[i]=,Head[i]=;
for(int i=;i<=m2;i++)
End[i]=Next[i]=;
}
bool topsort()
{
queue<int>q;
for(int i=;i<=n;i++)
if(fa[i]==i&&in[i]==)q.push(i);
while(q.size())
{
int x=q.front();q.pop();
for(int i=Head[x];i;i=Next[i])
{
int y=End[i];
in[y]--;
if(in[y]==)q.push(y);
}
}
for(int i=;i<=n;i++)
if(fa[i]==i&&in[i]>)return ;
return ;
}
int main()
{
scanf("%d",&T);
while(T--)
{
bool flag=;
int x,y;
scanf("%d%d%d",&n,&m1,&m2);
clear();
for(int i=;i<=m1;i++)//加无向边
{
scanf("%d%d",&x,&y);
if(get(x)==get(y))flag=;
//如果无向边就能构成环
else Union(x,y);
}
for(int i=;i<=m2;i++)
{
scanf("%d%d",&x,&y);
int fx=get(x),fy=get(y);
if(fx==fy)flag=;
add(fx,fy);
in[fy]++;
}
if(flag)puts("YES");
else{
bool f=topsort();
if(f)puts("YES");
else puts("NO");
}
}
return ;
}

拓扑排序复习——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. Eclipse中移除native层编译支持

    1. .project 文件 删除全部包括 org.eclipse.cdt前缀的xml结点元素.以下是一个包括cdt的完整编译配置文件. <?xml version="1.0" ...

  2. Json实现异步请求(提交评论)

    主要将代码粘贴,通过阅读代码理解当中的相关逻辑. html代码: <form id="form1" runat="server"> <p> ...

  3. 猫猫学IOS(二)UI之button操作 点击变换 移动 放大缩小 旋转

    不多说,先上图片看效果,猫猫分享.必须精品 原创文章.欢迎转载.转载请注明:翟乃玉的博客 地址:viewmode=contents">http://blog.csdn.net/u013 ...

  4. 探索Scala(3)-- 单例对象

    研究一下Scala语言的单例对象(Singleton Objects),为下一篇文章做准备. static不是keyword 上一篇文章提到过,interface并非Scala语言keyword,能够 ...

  5. OpenGL之路(五)制作旋转飞机模型

    #include <gl/glut.h> #include <gl/GLU.h> #include <gl/GL.h> #pragma comment(lib, & ...

  6. DCOS之Mesos-DNS介绍

    DCOS的Mesos-DNS它主要提供域名解析服务,Mesos-DNS 在DCOS框架中支持服务发现,同意应用程序和服务通过域名系统(DNS)来相互定位.DCOS中的 Mesos-DNS充当的角色和在 ...

  7. 【bzoj3620】似乎在梦中见过的样子

    枚举左端点,对于每个右端点处理出以右端点为结尾最大长度使得从左端点开始的前缀等于以右端点结束的后缀,即next数组 然后一直往前跳,直到长度小于子串长度的一半为止. #include<algor ...

  8. babel的安装和使用方法

    要使用Babel, 我们需要nodeJS的环境和npm, 主要安装了nodeJS, npm就默认安装了 , 现在安装nodeJS很简单了, 直接下载安装就好了: 安装es-checker 在使用Bab ...

  9. kbmMemTable关于内存表的使用,以及各种三层框架的评价

    关于内存表的使用(kbmMemTable) 关于内存表的使用说明一. Delphi使用内存表1.1 Delphi创建内存表步骤:1. 创建一个Ttable实例.2. 设置一个DataBaseName为 ...

  10. dedecms中去除首页index.html的方法

    本文介绍了dedecms中去除首页index.html的方法,有需要的朋友参考下. dedecms织梦cms建站程序输入地址后,而打开的实际地址后面有个index.html.   这里分享下两种解决方 ...