题意:假定有n个变量,还有m个二元组(u,v),分别表示变量y小于v。那么,所有变量都从小到大排列起来应该是什么样子呢?例如有4个变量a,b,c,d,若已知a<b,c<b,d<c,则这四个变量的排序可能是a<d<c<b。尽管还有其他可能(d<a<c<b),你只需要输出其中一个即可。
【分析】
把每个变量看成一个点,“小于”关系看成有向边,则可能得到了一个有向图。这样我们的任务实际上是把一个图的所有结点排序,使得每一条有向边(u,v)对应的u都排在v的前面。在图论中,这个问题称为拓扑排序(topological sort)。
不难发现:如果图中存在有向环,则不存在拓扑排序,反之则存在。不包含有向环的有向图称为有向无环图(Directed Acyclic Graph ,DAG)。可以借助DFS完成拓扑排序:在访问完一个结点之后把它加到当前拓扑序的首部(想一想为什么不是尾部)。
c[u] = 1表示u结点已经dfs结束,c[u] = -1表示u结点还没被访问完(在访问他的子孙结点),c[u]=0表示还没访问过。
基于dfs:
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
int n, m, t, topo[MAXN], G[MAXN][MAXN], c[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;
return true;
}
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) && (n+m))
{
memset(G, 0, sizeof(G));
for (int i = 0; i < m; i++)
{
int u, v; scanf("%d%d", &u, &v);
G[u][v] = 1;
}
bool ok = toposort();
for (int i = 1; i <= n; i++)
{
printf("%d", topo[i]);
if (i != n) printf(" ");
else printf("\n");
}
}
return 0;
}
/*
5 4
1 2
2 3
1 3
1 5
0 0
*/
kahn算法(基于BFS):
统计每个点的出度和入度,每次把入度为0的点删除,并删除以其为顶点的边(即所有终点入度--),把入度为0的点放入队列。
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 105;
int n, m, indegree[MAXN], G[MAXN][MAXN];
vector<int> topo;
void toposort(vector<int>& topo)
{
topo.clear();
queue<int> Q;
for (int i = 1; i <= n; i++)
{
if (!indegree[i]) Q.push(i);
}
while (!Q.empty())
{
int u = Q.front(); Q.pop();
topo.push_back(u);
for (int v = 1; v <= n; v++)
{
if (G[u][v])
{
if (--indegree[v] == 0) Q.push(v);
}
}
}
}
int main()
{
while (~scanf("%d%d", &n, &m) && (n+m))
{
memset(indegree, 0, sizeof(indegree));
memset(G, 0, sizeof(G));
for (int i = 0; i < m; i++)
{
int u, v; scanf("%d%d", &u, &v);
G[u][v] = 1;
indegree[v]++;
}
toposort(topo);
for (int i = 0; i < topo.size(); i++)
{
printf("%d", topo[i]);
if (i != topo.size()-1) printf(" ");
else printf("\n");
}
}
return 0;
}
/*
5 4
1 2
2 3
1 3
1 5
0 0
*/