一、基本算法

拓扑序列:对于一张有向图,求一个序列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. Jmeter使用Http代理服务器报DNSName components must begin with a letter的错

    最近了解到JMeter可以实现app的性能测试,需要借助JMeter的Http代理服务器来录制脚本. 于是,就按着网上的教程来进行操作,然而出师不利啊,刚启动就报错

  2. scrollTo(String text) and scrollToExact(String text) method of Android Driver not working

    Using the scrollTo(String text) and scrollToExact(String text) method of Android Driver. However the ...

  3. Kafka知识点汇总

    整体结构 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZXJpY19zdW5haA==/font/5a6L5L2T/fontsize/400/fill/I ...

  4. 使用JWT设计SpringBoot项目api接口安全服务

    转载直: 使用JWT设计SpringBoot项目api接口安全服务

  5. 0 lrwxrwxrwx. 1 root root 13 Nov 20 12:44 scala -> scala-2.12.4

    符号链接的文件属性相同,真正的权限属性由符号链接所指向的实际文件决定.

  6. CSS3 的10种Loading

    昨晚用CSS3实现了几种常见的Loading效果,虽然很简单,但还是分享一下,顺便也当是做做笔记…… 第1种效果: 代码如下: <div class="loading"> ...

  7. 解决ubuntu10.04不能上网

    1:命令行输入:lspci查看驱动,最后几行如果有ethernet controller:atheros communications ar8151 v1.0*的话,就说明驱动没有安装好, 2:下载地 ...

  8. (linux)likely和unlikely函数

      在Linux内核中likely和unlikely函数有两种(只能两者选一)实现方式,它们的实现原理稍有不同,但作用是相同的,下面将结合linux-2.6.38.8版本的内核代码来进行讲解. 1.对 ...

  9. RK3288 GPIO 输出问题【转】

    本文转载自:http://m.blog.csdn.net/jiangdou88/article/details/50158673 #define GPIO_BANK0              (0 ...

  10. local_irq_disable

    local_irq_disable 仅仅是 设置 当前CPU 的中断屏蔽位 disable_irq 是禁用 全部cpu 中断(只是当前irq) 如果你要禁止所有的中断该怎么办? 在2.6内核中,可以通 ...