拓扑排序
拓扑排序 百度百科:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
拓扑排序的操作:
- 找到图中入度为0的一个点。
- 删除该点和该点连接的边。
- 循环一二步骤,直到图中没有点或者不存在入度为0的点,需要注意的是,第二中情况表明图中存在环。
因此,拓扑排序(Topological sort)的前提是图为有向无环图(Dericted acyclic graph)。
操作很简单,找点、删边即可,上题中存图用邻接矩阵、邻接表、链式前向星都可,我用的是链式前向星。
AC代码:
ll degree[510];
ll head[510];
ll ct = 1;
struct node
{
ll v, next;
} edge[250010];
void add(ll u, ll v)
{
edge[ct].v = v;
edge[ct].next = head[u];
head[u] = ct++;
}
ll n, m;
ll ans[510], cot = 1;
void tuopu()
{
while (1)
{
ll now;
for (int i = 1; i <= n; i++)
{
if (!degree[i])
{
now = i;
degree[i] = 1;
break;
}
}
ans[cot++] = now;
now = head[now];
for (int i = now; i; i = edge[i].next)
degree[edge[i].v]--;
if (cot > n)
break;
}
}
int main()
{
while (cin >> n >> m)
{
cot = ct = 1;
fill(head, head + 501, 0);
fill(degree, degree + 501, 0);
for (int i = 1; i <= m; i++)
{
ll a, b;
cin >> a >> b;
degree[b]++;
add(a, b);
}
tuopu();
cout << ans[1];
for (int i = 2; i < cot; i++)
cout << ' ' << ans[i];
cout << endl;
}
return 0;
}
上面的写法时间复杂度最坏 O ( n 2 ) O(n^2) O(n2),可以用队列维护入度为0的结点信息,时间复杂度降至 O ( n ) O(n) O(n)。
核心代码:
int rk[100010], cnt = 0;
void topological()
{
int id = 1;
for (int i = 1; i <= n; i++)
if (!degree[i])
rk[++cnt] = i, vis[i] = 1;
while (id <= n)
{
int pos = rk[id++];
for (int i = head[pos]; i; i = edge[i].next)
{
int v = edge[i].v;
degree[v]--;
if (degree[v] == 0)
rk[++cnt] = v, vis[v] = 1;
dp[v] = max(dp[v], dp[pos] + 1);
}
}
}