假设有n个任务,有m个有序对(u,v),表示任务u应该排在任务v之前,那么怎样将这些任务按照顺序排列起来呢?比如有三个有序对(1,4),(3,2),(1,3)排列起来就是1,3,2,4 。尽管还有其他可能(如1,4,3,2),但我们只需找出一种即可,注意:有些情况无法排序,如(1,2),(2,3),(3,1)。
我们把每个任务看成一个点,将每个有序对看成有向边,则形成了一个有向图,由题意可知这个有向图必须是无环的,否则无法排序。在图论中,我们把对这些节点进行排序的问题称为拓扑排序,可借用dfs算法解决该问题。
代码如下:
#include<iostream>
#include<cstring>
using namespace std;
int n,m,topo[100]; //topo数组用来储存最终形成的拓扑序列
int G[100][100]; //储存有序对信息
int c[100]; //储存每个节点是否被访问过的信息
int t;
bool dfs(int u)
{
c[u]=-1; //该段代码的一个亮点,表示u节点正在被访问
for(int v=1;v<=n;v++)
{
if(G[u][v])
{
if(c[v]==-1) //访问到正在访问的节点,即为存在有向环
return false;
if(c[v]==0)
{
if(!dfs(v)) //深度优先遍历
return false;
}
}
}
c[u]=1; //返回时将该节点标记为已访问过
topo[–t]=u; //将此节点插入拓扑序列
return true;
}
int main()
{
int a,b;
cin>>n>>m;
t=n;
memset(G,0,sizeof(G));
memset(c,0,sizeof(c));
for(int i=0;i<m;i++)
{
cin>>a>>b;
G[a][b]=1;
}
for(int u=1;u<=n;u++)
{
if(!c[u]) //如果该节点没有被访问过
if(!dfs(u)) //dfs函数对图中节点进行深度优先遍历,返回值表示拓扑排序是否存在
{cout<<“存在有向环,失败退出”<<endl;
return 0;}
}
for(int i=0;i<n;i++)
cout<<topo[i]<<” “<<endl;
return 0;
}
即使我加了好多好多注释,可对于新手玩家而言还是有些难理解,那么我再举个例子。
比如两个有序对(1,2),(1,3),此时这三个节点形成一棵树,父节点为1,子节点为2和3,我们dfs对它进行访问。
首先进入父节点1,标记为正在访问,然后进入2,由于2不存在子节点,保存2并退回父节点,又访问3,同样保存并退回,此时1才算访问结束,在拓扑序列中插入到2和3的前面。
从2或者3开始结果也是一样的哦,大家不妨试一试。