假设有n个变量,还有m个二元组(u,v),分别表示变量u小于v。那么,所有变量从小到大排列起来应该是什么样子呢?例如,有4个变量a,b,c,d,若已知a<b,c<b,d<c,则这4个变量的排序可能是a<d<c<b。尽管还有其他可能(如d<a<c<b),你只需找出其中一个即可。
【分析】
题目中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的结点是否有父结点,所以如果前面还有结点肯定都比现在访问到的这些结点要小必须放在它们前面。
【代码】
#include<cstdio>
#include<cstring>
const int maxn = 100 + 5;
int n, m, t, G[maxn][maxn], c[maxn], topo[maxn];
bool dfs(int u)
{
c[u] = -1;//设置访问标志
for (int v = 1; v <= n; v++)
{
if (G[u][v])
{
if (c[v] < 0)return false;//存在有向环
else if (!c[v] && !dfs(v))return false;
}
}
c[u] = 1;//访问结束
topo[--t] = u;
}
bool toposort()
{
t = n;
memset(c, 0, sizeof(c));
for (int u = 1; u <= n; u++)
{
if (!c[u])
{
if (!dfs(u))return false;
}
}
return true;
}
int main()
{
while (scanf("%d%d", &n, &m) == 2 && n)
{
memset(G, 0, sizeof(G));
for (int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
G[u][v] = 1;
}
toposort();
printf("%d", topo[0]);
for (int i = 1; i < n; i++)
printf(" %d", topo[i]);
printf("\n");
}
return 0;
}