搜索与图论1.1-拓扑排序
一、简述
本节介绍一下拓扑排序的应用以及模板。
二、拓扑序
有向无环图,又称拓扑图,而拓扑图一定对应着拓扑序,比如有向图 \((1,2),(2,3),(1,3)\),前者指向后者,对应于拓扑序 \(1,2,3\)。基本模板类似 \(BFS\),如下
初始化队列 queue
所有入度为0的点入 queue
while queue不空
{
取出队头元素t
枚举t的所有出边j
删除t到j并且j的入度减一
如果j的入度为0
j入队
}
如果队列中的元素个数为点的个数,则有拓扑序
模板题AcWing848.有向图的拓扑序列
题目描述
给定一个 \(n\) 个点 \(m\) 条边的有向图,点的编号是 \(1\) 到 \(n\),图中可能存在重边和自环。
请输出任意一个该有向图的拓扑序列,如果拓扑序列不存在,则输出 \(−1\)。
若一个由图中所有点构成的序列 \(A\) 满足:对于图中的每条边 \((x,y)\),\(x\) 在 \(A\) 中都出现在 \(y\) 之前,则称 \(A\) 是该图的一个拓扑序列。
输入格式
第一行包含两个整数 \(n\) 和 \(m\)。
接下来 \(m\) 行,每行包含两个整数 \(x\) 和 \(y\),表示存在一条从点 \(x\) 到点 \(y\) 的有向边 \((x,y)\)。
输出格式
共一行,如果存在拓扑序列,则输出任意一个合法的拓扑序列即可。
否则输出 \(−1\)。
数据范围
\(1≤n,m≤10^5\)
输入样例
3 3
1 2
2 3
1 3
输出样例
1 2 3
解题思路
直接套板子就行。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 100010, M = 100010;
int n, m;
int h[N], e[M], ne[M], idx;
int q[N], hh, tt = -1;
int d[N];
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
bool topSort()
{
for (int i = 1; i <= n; i ++)
if (d[i] == 0)
q[++ tt] = i;
while (hh <= tt)
{
int t = q[hh ++];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
d[j] --;
if (d[j] == 0) q[++ tt] = j;
}
}
return tt == n - 1;
}
int main()
{
memset(h, -1, sizeof h);
scanf("%d%d", &n, &m);
while (m --)
{
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
d[y] ++;
}
if (topSort())
for (int i = 0; i <= tt; i ++)
printf("%d ", q[i]);
else printf("-1");
return 0;
}
AcWing3696.构造有向无环图
题目描述
给定一个由 \(n\) 个点和 \(m\) 条边构成的图。
不保证给定的图是连通的。
图中的一部分边的方向已经确定,你不能改变它们的方向。
剩下的边还未确定方向,你需要为每一条还未确定方向的边指定方向。
你需要保证在确定所有边的方向后,生成的图是一个有向无环图(即所有边都是有向的且没有有向环的图)。
输入格式
第一行包含整数 \(T\),表示共有 \(T\) 组测试数据。
每组数据第一行包含两个整数 \(n,m\)。
接下来 \(m\) 行,每行包含三个整数 \(t,x,y\),用来描述一条边的信息,其中 \(t\) 表示边的状态,如果 \(t=0\),则表示边是无向边,如果 \(t=1\),则表示边是有向边。\(x,y\) 表示这条边连接的两个端点,如果是有向边则边的方向是从 \(x\) 指向 \(y\)。
保证图中没有重边(给定了 \((x,y)\),就不会再次出现 \((x,y)\) 或出现 \((y,x)\))和自环(不会出现 \(x=y\) 的情况)。
输出格式
对于每组数据,如果无法构造出有向无环图,则输出一行 NO。
否则,先输出一行 YES,随后 \(m\) 行,每行包含两个整数 \(x,y\),用来描述最终构造成的有向无环图中的每条边的具体方向(\(x\) 指向 \(y\)),边的先后顺序随意。
注意,已经确定方向的边,不能更改方向。
如果答案不唯一,输出任意合理方案均可。
数据范围
对于前三个测试点,\(1≤n,m≤10\)。
对于全部测试点,\(1≤T≤20000,2≤n≤2×10^5,1≤m≤min(2×10^5,\frac{n(n−1)}{2}),0≤t≤1,1≤x,y≤n\)。
保证在一个测试点中,所有 \(n\) 的和不超过 \(2×10^5\),所有 \(m\) 的和不超过 \(2×10^5\)。
输入样例
4
3 1
0 1 3
5 5
0 2 1
1 1 5
1 5 4
0 5 2
1 3 5
4 5
1 1 2
0 4 3
1 3 1
0 2 3
1 2 4
4 5
1 4 1
1 1 3
0 1 2
1 2 4
1 3 2
输出样例
YES
3 1
YES
2 1
1 5
5 4
2 5
3 5
YES
1 2
3 4
3 1
3 2
2 4
NO
解题思路
首先构造有向无环图,那么我们就想到拓扑排序。首先我们先不考虑无向边,只考虑有向边,我们先对有向边构造拓扑序,如果无法构造拓扑序,那么无论是否考虑剩余的无向边,都无法构造拓扑序。那么如果可以构造,剩余的无向边,按照拓扑序加进即可。
C++代码
#include <bits/stdc++.h>
using namespace std;
const int N = 200010, M = 200010;
int T;
int n, m;
int h[N], e[M], ne[M], idx;
int q[N];
int d[N];
struct Edge
{
int a, b;
} edge[M];
int pos[N];
void add(int x, int y)
{
e[idx] = y;
ne[idx] = h[x];
h[x] = idx ++;
}
bool topSort()
{
int hh = 0, tt = -1;
for (int i = 1; i <= n; i ++)
if (!d[i])
q[++ tt] = i;
while (hh <= tt)
{
int t = q[hh ++];
for (int i = h[t]; ~i; i = ne[i])
{
int j = e[i];
d[j] --;
if (!d[j]) q[++ tt] = j;
}
}
return tt == n - 1;
}
int main()
{
scanf("%d", &T);
while (T --)
{
scanf("%d%d", &n, &m);
memset(h, -1, (n + 1) * 4);
memset(d, 0, (n + 1) * 4);
idx = 0;
int cnt = 0;
while (m --)
{
int t, x, y;
scanf("%d%d%d", &t, &x, &y);
if (t == 0) edge[cnt ++] = {x, y};
else
{
add(x, y);
d[y] ++;
}
}
if (!topSort()) puts("NO");
else
{
puts("YES");
for (int i = 1; i <= n; i ++)
for (int j = h[i]; ~j; j = ne[j])
printf("%d %d\n", i, e[j]);
for (int i = 0; i < n; i ++) pos[q[i]] = i;
for (int i = 0; i < cnt; i ++)
{
int a = edge[i].a, b = edge[i].b;
if (pos[a] > pos[b]) swap(a, b);
printf("%d %d\n", a, b);
}
}
}
return 0;
}
本文来自博客园,作者:Cocoicobird,转载请注明原文链接:https://www.cnblogs.com/Cocoicobird/p/17185890.html
浙公网安备 33010602011771号