Loading

拓扑排序学习笔记

About

有向无环图(DAG)

  • 有向图:由顶点和有向边组成的图,边具有方向性。
  • 无环图:图中不存在任何有向环路。
  • 拓扑排序:只适用于有向无环图(DAG),用于确定顶点的线性顺序,使得对于每一条有向边 \((u, v)\),顶点 \(u\) 在顶点 \(v\) 之前。

拓扑排序的定义

给定一个有向无环图 \(G = (V, E)\),拓扑排序是将图中所有顶点排成一个线性序列,使得对于每一条有向边 \((u, v)\),顶点 \(u\) 在顶点 \(v\) 之前。

示例

假设有以下有向图:

1 -> 2 -> 3
1 -> 4
4 -> 3

一个合法的拓扑排序是:1, 2, 4, 31, 4, 2, 3


拓扑排序的原理

拓扑排序的核心思想是通过不断移除入度为 0 的顶点,并更新其邻接顶点的入度,直到所有顶点都被移除或无法继续移除。

算法步骤

  1. 初始化
    • 计算每个顶点的入度(即指向该顶点的边的数量)。
    • 将所有入度为 0 的顶点加入队列。
  2. 处理队列
    • 从队列中取出一个顶点,将其加入拓扑排序结果。
    • 遍历该顶点的所有邻接顶点,将其入度减 1。如果某个邻接顶点的入度变为 0,则将其加入队列。
  3. 结束条件
    • 如果所有顶点都被处理,则拓扑排序成功。
    • 如果队列为空但仍有未处理的顶点,则图中存在环,拓扑排序失败。

T1

//纯板子判环
#include <bits/stdc++.h>
using namespace std;
const int N = 1010,M = 2010;
struct node{
	int to,nxt;
}a[M];
int pre[N],in[N];
queue <int> q;
int n,m;
int k;
void add(int u,int v){
	a[++k] = {v,pre[u]};
	pre[u] = k;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		int cnt = 0;
		memset(in,0,sizeof in);
		memset(pre,0,sizeof pre);
		k = 0;
		while(!q.empty()) q.pop();
		cin>>n>>m;
		for(int i = 1;i <= m;i++){
			int x,y;
			cin>>x>>y;
			add(x,y);
			in[y]++;
		}
		for(int i = 1;i <= n;i++){
			if(in[i] == 0){
				q.push(i);
				cnt++;
			}
		}
		while(!q.empty()){
			int h = q.front();
			q.pop();
			for(int i = pre[h];i;i = a[i].nxt){
				int to = a[i].to;
				in[to]--;
				if(in[to] == 0){ 
					q.push(to);
					cnt++;
				}
			}
		}
		bool fl = 1;
		for(int i = 1;i <= n;i++) if(in[i] != 0) fl = 0;
		if(fl) cout<<"N\n";
		else cout<<"Y\n";
	}
}

T2

  • 拓扑排序+DP
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10,M = 5e4+10;
struct node{
	int to,nxt;
}a[M];
int pre[N],in[N],c[N],dp[N],k;
int n,m;
void add(int u,int v){
	a[++k] = {v,pre[u]};
	pre[u] = k;
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i = 1;i <= n;i++){
		cin>>c[i];
	}
	for(int i = 1;i <= m;i++){
		int x,y;
		cin>>x>>y;
		add(x,y);
		in[y]++;
	}
	queue<int> q;
	int cnt = 0;
	for(int i = 1;i <= n;i++){
		if(in[i] == 0) {
			q.push(i);
			cnt++;
			dp[i] = c[i];
		}
	}
	while(!q.empty()){
		int h = q.front();
		q.pop();
		for(int i = pre[h];i;i = a[i].nxt){
			//使用当前节点松弛其他节点
			int to = a[i].to;
			dp[to] = max(dp[to],dp[h]+c[to]);
			in[to]--;
			if(in[to] == 0) q.push(to);
		}
	}
	int ans = 0;
	for(int i = 1;i <= n;i++){
		ans = max(ans,dp[i]);
	}
	cout<<ans<<endl;
}

T3

T2 Plus

/*
 * @Author    FrankWKD (wangkedi01)
 * @Date      2025-06-09
 * @Source    https://oj.czos.cn/contest/problem?id=24562&pid=2
 * @FileName  T3.cpp
 * @FilePath  /media/frank/FrankW/_default/_Mine!/Working/code-spaces/czos/TopologicalSorting/T3.cpp
 * @Note      本质上就是树上DP,优化了一下这个DP的顺序。
 */

// #pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10;
int in[N], out[N], x, y, n, m, pre[N], clonein[N], k;
int dp[N];
struct node {
    int to, nxt;
} a[M];

void add(int u, int v) {
    a[++k] = {v, pre[u]};
    pre[u] = k;
}
int main() {
    // ios::sync_with_stdio(false);
    // cin.tie(0); cout.tie(0);
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        cin >> x >> y;
        add(x, y);
        in[y]++;
        clonein[y]++;
        out[x]++;
    }
    queue<int> q;
    for (int i = 1; i <= n; i++) {
        if (in[i] == 0) {
            q.push(i);
            dp[i] = 1;
        }
    }
    while (!q.empty()) {
        int h = q.front();
        q.pop();
        for (int i = pre[h]; i; i = a[i].nxt) {
            int to = a[i].to;
            dp[to] += dp[h];
            in[to]--;
            if (in[to] == 0) {
                q.push(to);
            }
        }
    }
    int tot = 0;
    for (int i = 1; i <= n; i++) {
        if (clonein[i] != 0 and out[i] == 0) {
            tot += dp[i];
        }
    }
    cout << tot << endl;
    return 0;
}
posted @ 2025-06-08 14:06  FrankWkd  阅读(41)  评论(0)    收藏  举报