一、基本算法

拓扑序列:对于一张有向图,求一个序列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. VBscript 做的设置网卡名称

    Set WSHShell=WScript.CreateObject("WScript.Shell") Dim NetcardDescriptionDim NetcardName i ...

  2. C#实现模拟登录百度并发送私信

    首先获取Token,根据Token获取PubliKey,使用RSA加密POST数据 private Regex _regex = new Regex(@"\{.*\}", Rege ...

  3. iOS_40_核心动画

    核心动画之CATransition转场动画 终于效果图: 核心动画之CAKeyFrameAnimation,图标抖动效果 终于效果图: 核心动画之CAAnimationGroup(动画组) 终于效果图 ...

  4. 把握linux内核设计思想(五):下半部机制之工作队列及几种机制的选择

    [版权声明:尊重原创.转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途]         工作队列是下半部的第二种将工作推后运行形式.和软中断.task ...

  5. CPU维修技术

    中央处理单元(Central Process Unit)简称CPU,是电脑的核心部件,负责处理和运算电脑内部所有数据.因其在电脑中的地位相当重要,所以一旦发生故障就会造成很严重的后果. [技术66]开 ...

  6. Bean定义并注册到spring

    1.XML配置文件 2.Annotation注解 3.Java Code 配置方式 BeanDefinitionRegistryPostProcessor

  7. 用NHibernate处理带属性的多对多关系

    1.引言 老谭在面试开发者的时候,为了考察他们的数据库开发能力,经常祭出我的法宝,就是大学数据库教程中讲到的一个模式:学生选课.这个模式是这种: 在这个模式中,学生(Student)和课程(Cours ...

  8. 【健康生活】Google、百度之间的选择

    没有什么技术性的分析,仅仅是个人吐槽而已. 一般人遇到问题就会说一句"百度一下",说实话,百度在中国推广的真的非常不错,可谓是家喻户晓,搜索个八卦新闻,小文章,小电影什么的的确非常 ...

  9. Codeforces Round #422 (Div. 2) D. My pretty girl Noora 数学

    D. My pretty girl Noora     In Pavlopolis University where Noora studies it was decided to hold beau ...

  10. Do not use the <section> element as a generic container; this is what <div> is for, especially when the sectioning is only for styling purposes.

    Do not use the <section> element as a generic container; this is what <div> is for, espe ...