了解拓扑排序之前,先要清楚一个概念:DAG(Directed Acyclic Graph)——有向无环图,即不包含有向环的有向图。
在实际生活中常常会遇到这样的问题:在着手处理工作B之前先要处理完工作A和工作X,如何排列工作的顺序才能符合所有的条件?假若把每一种工作看作一个节点,工作顺序看作一条边,就可以得到一个有向无环图。将该图的节点进行排序,使得每一条有向边的起点都在终点之前,这种排序方式就是拓扑排序。
若图中存在有向环,则不可进行拓扑排序。
拓扑排序的主要步骤:
1.将入度为0的点输出
2.将该点连接的所有边删除
3.重复1,2两步直到所有点都输出
以下为模板代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn=100;//最大顶点数
int n,m;//n为顶点数,m为约束条件数量
int tol;//加边时统计边数
int indeg[maxn];//存储每个点的入度,注意每个样例都要初始化为0
struct node
{
int u,v;//u为起点,v为终点
}edge[maxn*maxn];
void addedge(int u,int v)
{
edge[tol].u=u;
edge[tol].v=v;
indeg[v]++;//统计入度
}
int topo()
{
queue <int> q;
int cnt=0;//统计已排序的点的个数
for(int i=0;i<n;i++)//注意编号,此处编号从0开始
{
if(indeg[i]==0)//入度为0的点入列
q.push(i);
}
while(!q.empty())
{
int now=q.front();
q.pop();
cnt++;
for(int i=0;i<tol;i++)
{
if(edge[i].u==now)
{
indeg[edge[i].v]--;//翻边
if(indeg[edge[i].v]==0)//如果入度为0,入队
q.push(edge[i].v);
}
}
}
if(cnt==n)//拓扑序存在
return 1;
else
return 0;
}
当然,也可用二维数组直接存图。然而,当数据很大的时候,以上代码有可能超时(二维数组存储会爆内存),所以利用不定长数组vector进行了一点优化。
下面放代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=100000;//最大顶点数
vector <int> pic[maxn];
int indeg[maxn];//储存入度
int main()
{
int n,m;
int begin,end;
while(~scanf("%d%d",&n,&m)&&(n+m))
{
int cnt=0;//计数
for(int i=0;i<=n;i++)//清空
pic[i].clear();
memset(indeg,0,sizeof(indeg));//初始化
while(m--)
{
scanf("%d%d",&begin,&end);
indeg[end]++;//统计入度
pic[begin].push_back(end);//加边
}
queue <int> q;
for(int i=0;i<n;i++)
{
if(indeg[i]==0)//入度为0的点入队
q.push(i);
}
while(!q.empty())
{
int now=q.front();
q.pop();
cnt++;
for(int i=0;i<pic[now].size();i++)
{
indeg[pic[now][i]]--;
if(!indeg[pic[now][i]])
q.push(pic[now][i]);
}
}
if(cnt==n)
printf("YES\n");
else
printf("NO\n");
}
return 0;
}
参考书籍:紫书,挑战程序设计竞赛2