算法竞赛笔记——图论

图论

最短路

Floyd \(O(n^3)\)

/*
一般用于n < 500的图
前提条件:图权值可以为负,但不能有负权值回路,负权值回路可能不存在最短路径
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 250;
constexpr int INF = 0x3f3f3f3f;

int f[MAXN][MAXN]; 

void init(int n){
    for(int i = 1; i <= n; i ++) {
        for(int j = 1; j <= n; j ++) {
            f[i][j] = INF;
        }
    }
    for(int i = 1; i <= n; i ++) {
        f[i][i] = 0;
    }    
}

void AddEdge(int u, int v, int w) {
    f[u][v] = min(f[u][v], w);
    f[v][u] = min(f[v][u], w);
}

void Floyd(){
    for (int k = 1; k <= n; k ++) {  // 中转点
        for (int x = 1; x <= n; x ++) { // 起点
            for (int y = 1; y <= n; y ++) {  // 终点
            	f[x][y] = min(f[x][y], f[x][k] + f[k][y]);
            }
        }
    }    
}

inline void solve() {
	int n, m;
	cin >> n >> m;
	init(n);
	for(int i = 1; i <= m; i ++) {
		int a, b, c;
		cin >> a >> b >> c;
		AddEdge(a, b, c);
	}
	Floyd();
}

Johnson \(O(n \cdot m \cdot logm)\)

/*
1.设置超级源点0,向 n 个点连边权为 0 的单向边,用BellmanFord计算势能h[N]
2.通过势能调整边权
3.对每个点跑dijstra
4.还原边权

d[u][v] = f[u][v] + h[u] - h[v]
边u到v的新权值 = 边u到v的真实值 + u的势能 - v的势能
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

constexpr int MAXN = 3e3 + 100;
constexpr ll INF = 1e9;

int n, m;

struct Edge{
	int u, v;
	ll w;
};

vector<Edge> edges;  // 存所有的边
ll h[MAXN];  // 势能数组

bool BellmanFord(){
	for(int i = 1; i <= n; i ++) h[i] = INF;
	h[0] = 0ll;
	for(int i = 1; i <= n; i ++) {
		for(const auto &temp : edges) {
			int u = temp.u, v = temp.v;
			ll w = temp.w;
			if(h[u] == INF) continue;
			if(h[v] > h[u] + w) h[v] = h[u] + w;
		}
	}
	for(const auto &temp : edges) {
		int u = temp.u, v = temp.v;
		ll w = temp.w;
		if(h[u] == INF) continue;
		if(h[v] > h[u] + w) return false;
	}
	return true;
}

struct Node{
	int x;
	ll w;
	// 小根堆
	bool operator < (const Node &u) const {
		return (w == u.w) ? (x < u.x) : (w > u.w);
	}
}; 

vector<Node> g[MAXN];  // 邻接表
ll dis[MAXN][MAXN];

void dijstra(int st){
	static ll d[MAXN];
	for(int i = 1; i <= n; i ++) d[i] = INF;
	
	priority_queue<Node> pq;
	pq.push({st, d[st] = 0});
	
	bitset<MAXN> vis;
	while(!pq.empty()) {
		int x = pq.top().x;
		pq.pop();
		if(vis[x]) continue;
		vis[x] = true;
		for(const auto &temp : g[x]) {
			int y = temp.x;
			ll w = temp.w;
			if(d[y] > d[x] + w) {
				d[y] = d[x] + w;
				pq.push({y, d[y]});
			}
		}
	}
	// 恢复权值
	for(int i = 1; i <= n; i ++) {
		if(d[i] == INF) dis[st][i] = d[i];
		else dis[st][i] = d[i] - h[st] + h[i];
	}
}


inline void solve() {
	cin >> n >> m;

	for(int i = 1; i <= m; i ++) {
		int u, v;
		ll w;
		cin >> u >> v >> w;
		edges.push_back({u, v, w});
		g[u].push_back({v, w});
	}

	// 建立超级源点
	for(int i = 1; i <= n; i ++) {
		edges.push_back({0, i, 0ll});
	}

	//判负环
	if(!BellmanFord()) {
		cout << "-1\n";
		return;
	}

	//得到h势能数组后, re-weight 重新设置边权
	for(int u = 1; u <= n; u ++) {
		for(auto &temp : g[u]){
			temp.w = temp.w + h[u] - h[temp.x];
		}
	}

	//跑 n 次 dijstra
	for(int x = 1; x <= n; x ++) dijstra(x);
	
    //输出每个点到所有点的距离之和
	for(int i = 1; i <= n; i ++) {
		ll ans = 0;
		for(int j = 1; j <= n; j ++) ans += dis[i][j];
		cout << ans << '\n';
	}
}

LCA

倍增法

预处理 $O(n \cdot logn) $

查询 \(O(logn)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

inline void solve() {
	int n;
	cin >> n;
	vector<vector<int>> edge(n + 1);
	for(int i = 1; i < n; i ++){
		int u, v;
		cin >> u >> v;
		edge[u].push_back(v);
		edge[v].push_back(u);
	}
	vector<int> dep(n + 1);
	constexpr int b = 20;
	vector<vector<int>> fa(n + 1, vector<int>(b + 1));
	function<void(int, int)> dfs = [&](int u, int pa) {
		dep[u] = dep[pa] + 1;
		fa[u][0] = pa;
		for(int i = 1; i <= b; i ++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
		for(const auto &v : edge[u]) {
			if(v == pa) continue;
			dfs(v, u);
		}
	};
	dfs(1, 0);
	function<int(int, int)> LCA = [&](int x, int y) {
		if(dep[x] < dep[y]) swap(x, y);
		for(int i = b; i >= 0; i --) if(dep[fa[x][i]] >= dep[y]) x = fa[x][i];
		if(x == y) return x;
		for(int i = b; i >= 0; i --) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
		return fa[x][0];
	};
	int q;
	cin >> q;
	while(q --) {
		int a, b;
		cin >> a >> b;
		cout << LCA(a, b) << '\n';
	}
}

最大流

EK(封装)\(O(n \cdot m^2)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
constexpr int MAXN = 250;
constexpr int INF = 0x3f3f3f3f;

struct Edge {
	int from, to, cap, flow;
	Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};

struct EK {
	int n, m;     			// n:点数,m:边数
	vector<Edge> edges;		// edges:所有边的集合
	vector<int> G[MAXN];	//G:点 x -> x 的所有边在 edges 中的下标
	int a[MAXN], p[MAXN];	//a:点 x -> BFS 过程中最接近点 x 的边给它的最大流
							//p:点 x -> BFS 中最接近点 x 的边
	void init(int n) {
		this->n = n;
		for(int i = 0; i < n; i ++) G[i].clear();
		edges.clear();
	}

	void AddEdge(int from, int to, int cap) {
		edges.push_back(Edge(from, to, cap, 0));
		edges.push_back(Edge(to, from, 0, 0));
		m = edges.size();
		G[from].push_back(m - 2);
		G[to].push_back(m - 1);
	}

	ll Maxflow(int s, int t) {
		ll flow = 0;
		while(1){
			memset(a, 0, sizeof(a));
			queue<int> Q;
			Q.push(s);
			a[s] = INF;
			while(!Q.empty()){
				int x = Q.front();
				Q.pop();
				for(int i = 0; i < G[x].size(); i ++) {
					Edge& e = edges[G[x][i]];
					if(!a[e.to] && e.cap > e.flow) {
						p[e.to] = G[x][i];  	// G[x][i] 是最接近点 e.to 的边
						a[e.to] = min(a[x], e.cap - e.flow);
						Q.push(e.to);
					}
				}
				if(a[t]) break;		// 如果汇点接受到了流,就退出BFS
			}
			if(!a[t]) break; 	// 如果汇点没有接收到流,说明源点和汇点不在同一个连通分量上
			for(int u = t; u != s; u = edges[p[u]].from) {	// 通过 u 追寻 BFS 过程中 s -> t 的路径
				edges[p[u]].flow += a[t];			// 增加路径上边的 flow 值
				edges[p[u] ^ 1].flow -= a[t];		// 减小反向路径的 flow 值
			}
			flow += a[t];
		}
		return flow;
	}
};

inline void solve() {
	int n, m, s, t;
	cin >> n >> m >> s >> t;
    --s, --t;
	EK F;
	F.init(n);
	for(int i = 1; i <= m; i ++) {
		int a, b, c;
		cin >> a >> b >> c;
		--a, --b;
		F.AddEdge(a, b, c);
	}
	ll ans = F.Maxflow(s, t);
	cout << ans << '\n';
}

ISAP(封装) \(O(n^2 \cdot m)\)

/*
小心爆int!!!
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

constexpr int MAXN = 250;
constexpr int INF = 0x3f3f3f3f;

struct Edge {
	int from, to, cap, flow;
	Edge(int u, int v, int c, int f) : from(u), to(v), cap(c), flow(f) {}
};

bool operator < (const Edge& a, const Edge& b) {
	return a.from < b.from || (a.from == b.from && a.to < b.to);
}

struct ISAP {
	int n, m, s, t;     	// n:点数,m:边数,s:起点,t:终点
	vector<Edge> edges;		// edges:所有边的集合
	vector<int> G[MAXN];
	bool vis[MAXN];
	int d[MAXN];  			// 点在第几层
	int cur[MAXN];
	int p[MAXN];
	int num[MAXN];

	void init(int n) {
		this->n = n;
		for(int i = 0; i < n; i ++) G[i].clear();
		edges.clear();
	}

	void AddEdge(int from, int to, int cap) {
		edges.push_back(Edge(from, to, cap, 0));
		edges.push_back(Edge(to, from, 0, 0));
		m = edges.size();
		G[from].push_back(m - 2);
		G[to].push_back(m - 1);
	}

	bool BFS() {
		memset(vis, 0, sizeof(vis));
		queue<int> Q;
		Q.push(t);
		vis[t] = true;
		d[t] = 0;
		while(!Q.empty()) {
			int x = Q.front();
			Q.pop();
			for(int i = 0; i < G[x].size(); i ++) {
				Edge& e = edges[G[x][i] ^ 1];
				if(!vis[e.from] && e.cap > e.flow) {
					vis[e.from] = true;
					d[e.from] = d[x] + 1;
					Q.push(e.from);
				}
			}
		}
		return vis[s];
	}

	int Augment() {
		int x = t, a = INF;
		while(x != s) {
			Edge& e = edges[p[x]];
			a = min(a, e.cap - e.flow);
			x = edges[p[x]].from;
		}
		x = t;
		while(x != s) {
			edges[p[x]].flow += a;
			edges[p[x] ^ 1].flow -= a;
			x = edges[p[x]].from;
		}
		return a;
	}

	ll Maxflow(int s, int t) {
		this->s = s;
		this->t = t;
		ll flow = 0;
		BFS();
		memset(num, 0, sizeof(num));
		for(int i = 0; i < n; i ++) num[d[i]] ++;
		int x = s;
		memset(cur, 0, sizeof(cur));
		while(d[s] < n) {
			if(x == t) {
				flow += Augment();
				x = s;
			}
			int ok = 0;
			for(int i = cur[x]; i < G[x].size(); i ++) {
				Edge& e = edges[G[x][i]];
				if(e.cap > e.flow && d[x] == d[e.to] + 1) {
					ok = 1;
					p[e.to] = G[x][i];
					cur[x] = i;
					x = e.to;
					break;
				}
			}
			if(!ok){
				int m = n - 1;
				for(int i = 0; i < G[x].size(); i ++) {
					Edge& e = edges[G[x][i]];
					if(e.cap > e.flow) m = min(m, d[e.to]);
				}
				if(--num[d[x]] == 0) break;
				num[d[x] = m + 1] ++;
				cur[x] = 0;
				if(x != s) x = edges[p[x]].from;
			}
		}
		return flow;
	}
};

inline void solve() {
	int n, m, s, t;
	cin >> n >> m >> s >> t;
    --s, --t;
	ISAP MF;
	MF.init(n);
	for(int i = 1; i <= m; i ++) {
		int a, b, c;
		cin >> a >> b >> c;
		--a, --b;
		MF.AddEdge(a, b, c);
	}
	ll ans = MF.Maxflow(s, t);
}

Dinic(封装)

一般情况:\(O(n^2 \cdot m)\)

单位容量中:$O \left (m \cdot \min\left(m^{\frac{1}{2}}, n^{\frac{2}{3}}\right)\right) $ ;

二分图中:\(O(n^\frac{1}{2} \cdot m)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

constexpr int MAXN = 25000;
constexpr int INF = 0x3f3f3f3f;

struct DINIC {
	struct Edge {
		int v, nxt, cap, flow;
	} e[MAXN];

	int fir[MAXN], cnt = 0;

	int n, s, t;
	int d[MAXN], cur[MAXN];

	void init(int n) {
		this->n = n;
		memset(fir, -1, sizeof(fir));
		cnt = 0;
	}

	void AddEdge(int u, int v, int w) {
		e[cnt] = {v, fir[u], w, 0};
		fir[u] = cnt ++;
		e[cnt] = {u, fir[v], 0, 0};
		fir[v] = cnt ++;
	}

	bool BFS() {
		queue<int> Q;
		memset(d, 0, sizeof(int) * (n + 1));

		d[s] = 1;
		Q.push(s);
		while(!Q.empty()) {
			int u = Q.front();
			Q.pop();
			for(int i = fir[u]; ~i; i = e[i].nxt) {
				int v = e[i].v;
				if((!d[v]) && (e[i].cap > e[i].flow)) {
					d[v] = d[u] + 1;
					Q.push(v);
				}
			}
		}
		return d[t];
	}

	int DFS(int u, int flow) {
		if((u == t) || (!flow)) return flow;

		int ret = 0;
		for(int& i = cur[u]; ~i; i = e[i].nxt) {
			int v = e[i].v, a;
			if((d[v] == d[u] + 1) && (a = DFS(v, min(flow - ret, e[i].cap - e[i].flow)))) {
				ret += a;
				e[i].flow += a;
				e[i ^ 1].flow -= a;
				if(ret == flow) return ret;
			}
		}
		return ret;
	}

	ll Maxflow(int s, int t) {	
		ll maxflow = 0;
		this->s = s;
		this->t = t;		
		while(BFS()){
			memcpy(cur, fir, sizeof(int) * (n + 1));
			maxflow += DFS(s, INF);
		}
		return maxflow;
	}
} MF;

inline void solve() {
	int n, m, x;
	cin >> n >> m >> s >> t;
    --s, --t;
	MF.init(n);
	for(int i = 1; i <= m; i ++) {
		int a, b, c;
		cin >> a >> b >> c;
		--a, --b;
		MF.AddEdge(a, b, c);
	}
	ll ans = MF.Maxflow(s, t);
}
posted @ 2025-04-14 19:51  C₄H₈O  阅读(68)  评论(1)    收藏  举报