题目描述:
John有n个任务要做,每个任务在做之前要先做特定的一些任务。
输入第一行包含两个整数n和m,其中1<=n<=100。 n表示任务数,而m表示有m条任务之间的关系。 接下来有m行,每行包含两个整数i和j,表示任务i要在j之前做。
当读入两个0(i=0,j=0)时,输入结束。
输出包含q行,每行输出一条可行的安排方案。
解题思路:
把每个变量看成一个点,“小于”关系看成有向边,则得到了一个有向图。这样,我们的任务实际上是把一个图的所有结点排序,使得每一条有向边(u,v)对应的u都排在v的前面。
用dfs完成拓扑排序,在访问完一个结点之后吧它加到当前拓扑序的首部。
也就是说,题目中n个变量看成图中n个结点,图中的边即是二元关系,m个二元组就代表了这m条边表示的二元关系,(u,v)表示v大于u,从u向v连一条有向边,如果这m个二元
组不矛盾,那么这个图便是一个DAG否则就存在一个有向环,解便不存在了,举个例子a<b,b<c,可以推出a<c,但是现在出现了c连到了a意思是a>c,与之前的递推相
悖,所以如果图中存在有向环,则不存在拓扑序,反之一个DAG图就存在拓扑序,拓扑序可以用DFS求。从图中的某个结点开始,用一个c数组标记,-1表示这个结点正
在访问,那么如果再次访问到一个在c中被标记为-1的结点就说明存在一个有向环,0表示这个结点还未被访问,它的子孙有没有被访问不知道,因为有可能从它的子孙
开始DFS,1表示这个结点以及它的子孙已经被访问过,每次DFS在访问完一个结点后把它加到当前拓扑序首部,因为只能保证向后已经访问完,我们不知道开始DFS的
结点是否有父结点,所以如果前面还有结点肯定都比现在访问到的这些结点要小必须放在它们前面。
AC代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
const int max = 1200;
int c[max],topo[max],t,n,G[max][max];
//G(a,b)表示a指向b的有向线段
int dfs(int u)
{
int v;
c[u] = -1;//访问标志,-1是正在访问,0是未访问,1是已经访问
for(v = 1 ; v <= n ; v++)//看的是与你比较的v有无再次访问到
{
if(G[u][v])
{
if(c[v] < 0)//如果再次访问到被标记为-1的点,说明存在有向环
return 0;
else if(!c[v]&&!dfs(v))
return 0;
}
}
c[u] = 1;
topo[--t] = u;
return 1;
}
int toposort()
{
int i;
t = n;
memset(c,0,sizeof(c));
for(int i = 1 ; i <= n ; i++)
{
if(!c[i]&&!dfs(i))
return 0;
}
return 1;
}
int main()
{
int m , i;
while(scanf("%d %d",&n,&m),n||m)
{
int u , v;
memset(G,0,sizeof(G));
for(int i = 0 ; i < m ; i++)
{
scanf("%d %d",&u,&v);
G[u][v] = 1;
}
if(toposort())
{
for(i = 0 ; i < n-1 ; i++)
{
printf("%d ",topo[i]);
}
printf("%d\n",topo[i]);
}
}
return 0;
}