图论模板(部分)

news2025/5/18 9:28:00

图论模板(部分)

maincpp

#include <iostream>
#include <climits>
#include <limits>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();



int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    
    
    
    return 0;
}

最短路

Dijkstra:

struct Node {
	int y, v; // 连到哪里 边权
	Node(int _y, int _v) {y = _y; v = _v;};
};
int N; // 一共有多少个点
int n, m; // 多少个顶点 多少条边
std::vector<int> dist(N + 1, MAX_INT); // 每个点最短路径长度
std::vector<std::vector<Node>> edge(N + 1); // 1 ~ N
std::vector<bool> C(N + 1, false); // 

inline int dijkstra(int s, int t) { // 起点 终点
	std::fill(C.begin(), C.end(), false);
	std::fill(dist.begin(), dist.end(), MAX_INT);

	dist[s] = 0; // 初始化起点
	while(1) {
		int x = -1; // x 是不在C(点集)里面,并且离起点最近的那个点的下标

		for(int i = 1; i <= n; i++) { // 遍历n个点
			if(!C[i] && dist[i] < 1 << 30) { // 如果点集C里面没有这个点,并且能达到
				if(x == -1 || dist[i] < dist[x]) {
					 x = i;
				}
			}
		}

		if(x == t || x == -1) break; // 如果说x到达了终点t或者没有任何一个能达到的点就直接结束
		C[x] = true; // 加入到点集C
		for(const auto& i : edge[x]) dist[i.y] = std::min(dist[i.y], dist[x] + i.v); // 更新dist最小的点能达到的边的值
	}


	return dist[t];

}

Dijkstra (堆优化):

std::vector<std::vector<PII>> e;
std::vector<int> dist;
std::vector<bool> vis;
std::priority_queue<PII, std::vector<PII>, std::greater<PII>> q;

inline void Dijkstra() {
	q.push({0, s});
	dist[s] = 0;

	while(!q.empty()) {
		auto [val, u] = q.top();
		q.pop();

		if(vis[u]) continue;
		vis[u] = true;

		for(const auto&[v, w] : e[u]) {
			if(dist[v] > dist[u] + w) {
				dist[v] = dist[u] + w;
				q.push({dist[v], v});
			}
		}

	}

}

SPFA:

struct Edge {
	int x, y, v;
};
int n, m, k;
std::vector<int> dist;
std::vector<Edge> edge;
std::vector<int> vis;  // 标记节点是否在队列中
std::vector<int> cnt;      // 记录每个节点入队的次数,用于判断负环

inline void SPFA(int s, int t) {
	// 初始化距离数组
	std::fill(dist.begin(), dist.end(), INT_MAX);
	dist[s] = 0;
	
	// 初始化标记数组和入队次数数组
	std::fill(vis.begin(), vis.end(), false);
	std::fill(cnt.begin(), cnt.end(), 0);
	
	std::queue<int> q;
	q.push(s);
	vis[s] = true;
	cnt[s]++;
	
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = 0;
		
		rep(i, m) {
			if (edge[i].x == u) {
				int v = edge[i].y;
				int w = edge[i].v;
				if (dist[u] + w < dist[v]) {
					dist[v] = dist[u] + w;
					if (!vis[v]) {
						q.push(v);
						vis[v] = true;
						cnt[v]++;
						// 如果某个节点入队次数超过 n 次,说明存在负环
						if (cnt[v] > n) {
							std::cout << "-1\n";
							return;
						}
					}
				}
			}
		}
	}
	
	// 输出结果
	if (dist[t] < INT_MAX) std::cout << dist[t] << '\n';
	else std::cout << "-1\n";
}

拓扑排序:

int n;
std::vector<std::vector<int>> e;
std::vector<int> d;
std::queue<int> q;

inline void TopoSort(int x) {
	Rep(i, 1, n + 1) if(!d[i]) q.push(x);
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		std::cout << u << '\n';

		for(const int& v : e[u]) {
			if(--d[v] == 0) {
				q.push(v);
			}
		}
	}
}

Floyd:

int n;
std::vector<std::vector<int>> f, e;
std::vector<int> d;
std::queue<int> q;

inline void Floyd() {
	f.resize(n + 1, std::vector<int>(n + 1, INF));
	f = e;
	Rep(i, 1, n + 1) f[i][i] = 0;

	Rep(k, 1, n + 1) {
		Rep(i, 1, n + 1) {
			Rep(j, 1, n + 1) {
				if(f[i][k] < INF && f[k][j] < INF) {
					f[i][j] = std::min(f[i][j], f[i][k] + f[k][j]);
				}
			}
		}
	}
	
}

传递闭包(Floyd):

 inline void Floyd() {
 	Rep(k, 1, n + 1) {
 		Rep(i, 1, n + 1) {
 			Rep(j, 1, n + 1) {
 				e[i][j] |= (e[i][k] & e[k][j]);
 			}
 		}
 	}
 }

Floyd(bitset优化):

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <bitset>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();

int n;
std::vector<std::bitset<110>> e;

inline void Floyd() {
	Rep(j, 1, n + 1) Rep(i, 1, n + 1) if(e[i][j]) e[i] |= e[j];
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    
    std::cin >> n;
    e.resize(n + 1);
    Rep(i, 1, n + 1) {
        Rep(j, 1, n + 1) {
            int val;
            std::cin >> val;
            e[i][j] = val;
        }
    }
    
    Floyd();

    Rep(i, 1, n + 1) {
    	Rep(j, 1, n + 1) std::cout << e[i][j] << " ";
    	std::cout << '\n';
    }
    
    
    return 0;
}

Floyd求无向图最小环:

int MinCycle(int n) {
    int res = INF;
    vector<vector<int>> d(n + 1, vector<int>(n + 1, INF));
    Rep(i, 1, n + 1) d[i][i] = 0;
    Rep(i, 1, m + 1) {
        int u, v, w;
        cin >> u >> v >> w;
        d[u][v] = d[v][u] = min(d[u][v], w); // 处理重边
    }
    Rep(k, 1, n + 1) {
        rep(i, k) Rep(j, i + 1, k) res = min(res, d[i][j] + d[i][k] + d[k][j]);
        Rep(i, 1, n + 1) Rep(j, 1, n + 1) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    }
    return res == INF ? -1 : res; // 无环返回-1
}

差分约束:

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <queue>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();

int n, m;
std::vector<std::vector<PII>> e;
std::vector<int> dist, cnt;
std::vector<bool> vis;
std::queue<int> q;

inline void SPFA() {
	q.push(0);
	dist[0] = 0;
	vis[0] = true;
	cnt[0]++;

	while(!q.empty()) {
		int u = q.front();
		q.pop();
		vis[u] = false;

		for(const auto&[v, w] : e[u]) {
			if(dist[v] > dist[u] + w) {
				dist[v] = dist[u] + w;
				if(!vis[v]) {
					q.push(v);
					vis[v] = true;
					cnt[v]++;

					if(cnt[v] > n) {
						std::cout << "NO\n";
						exit(0);
					}
				}
			}
		}
	}
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    
    std::cin >> n >> m;
    dist.resize(n + 1, INF);
    e.resize(n + 1);
    vis.resize(n + 1, false);
    cnt.resize(n + 1, 0);

    Rep(i, 1, n + 1) e[0].push_back({i, 0});
    rep(i, m) {
    	int a, b, c;
    	std::cin >> a >> b >> c;
    	e[b].push_back({a, c});
    }

    SPFA();

    Rep(i, 1, n + 1) std::cout << dist[i] << " ";


    
    return 0;
}

prim:

int N, M, res = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> dist;
std::vector<bool> vis;

inline void prim() {
    dist[1] = 0;
    
    rep(i, N) {
        int t = -1;
        Rep(j, 1, N + 1) if (!vis[j] && (t == -1 || dist[j] < dist[t])) t = j;
        
        if (t == -1 || dist[t] == INF) break;
        vis[t] = true;
        res += dist[t];
        cnt++;
        
        Rep(j, 1, N + 1) if (!vis[j]) dist[j] = std::min(dist[j], e[t][j]);
        
    }
}

Kruskal:

struct edge {
	ll u, v, w;
	bool operator< (const edge& other) const{
		return w < other.w;
	}
};

ll N, K, res = 0;
std::vector<edge> e;
std::vector<ll> dist, fa;
std::unordered_map<ll, bool> mp;

ll Findest(ll x) {
	return x == fa[x] ? x : fa[x] = Findest(fa[x]);
}

inline void Kruskal() {
	std::sort(e.begin(), e.end());
	Rep(i, 1, N + 1) fa[i] = i;

	rep(i, e.size()) {
		ll u = e[i].u, v = e[i].v, w = e[i].w;
		ll fu = Findest(u), fv = Findest(v);
		if(fu == fv) continue;
		res += w;
		fa[fu] = fv;

	}

}

LCA(倍增):

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <cmath>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();

int N, M, S, LOG;
std::vector<std::vector<int>> e, fa;
std::vector<int> dep;

void DFS(int u, int father) {
    dep[u] = dep[father] + 1;
    fa[u][0] = father;
    for(int i = 1; i < LOG; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
    for(const int& v : e[u]) if(v != father) DFS(v, u);
}

inline int LCA(int u, int v) {
    if(dep[u] < dep[v]) std::swap(u, v);
    for(int i = LOG - 1; i >= 0; i--) if(dep[fa[u][i]] >= dep[v]) u = fa[u][i];
    if(u == v) return v;
    for(int i = LOG - 1; i >= 0; i--) if(fa[u][i] != fa[v][i]) u = fa[u][i],v = fa[v][i]; 
    return fa[u][0];
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    
    std::cin >> N >> M >> S;
    LOG = log2(N) + 1;
    e.resize(N + 1);
    fa.resize(N + 1, std::vector<int>(LOG, 0));
    dep.resize(N + 1, 0);
    
    rep(i, N - 1) {
        int x, y;
        std::cin >> x >> y;
        e[x].push_back(y);
        e[y].push_back(x); 
    }

    dep[S] = 0;
    DFS(S, 0);

    while(M--) {
        int x, y;
        std::cin >> x >> y;
        std::cout << LCA(x, y) << '\n';
    }
    
    return 0;
}

LCA(树链剖分):

void DFS1(int u, int father) {
	dep[u] = dep[father] + 1, sz[u] = 1, fa[u] = father;

	for(const int& v : e[u]) {
		if(v == father) continue;
		DFS1(v, u);
		sz[u] += sz[v];
		if(sz[son[u]] < sz[v]) son[u] = v;  
	}
}

void DFS2(int u, int t) {
	top[u] = t;
	if(!son[u]) return ;
	DFS2(son[u], t);
	for(const int& v : e[u]) if(v != fa[u] && v != son[u]) DFS2(v, v);
}

inline int LCA(int u, int v) {
	while(top[u] != top[v]) {
		if(dep[top[u]] < dep[top[v]]) std::swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

Kruskal重构树:

构建最大生成树,建立起来的父节点都是两点之间最小边权的最大值
构建最小生成树,建立起来的父节点都是两点之间最大边权的最小值

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <algorithm>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();

struct edge {
	int u, v, w;
	bool operator> (const edge& other) const{
		return w > other.w;
	}
};

int n, m, q;
std::vector<edge> adj;
std::vector<std::vector<int>> e;
std::vector<int> dist, sz, dep, son, top, fa, F, cost;

inline void Init() {
    std::cin >> n >> m;

	sz.resize(2 * n + 1, 0);
	dep.resize(2 * n + 1, 0);
	fa.resize(2 * n + 1, 0);
	F.resize(2 * n + 1, 0);
	son.resize(2 * n + 1, 0);
	cost.resize(2 * n + 1, -1);
	top.resize(2 * n + 1, 0);
	e.resize(2 * n + 1);
	
}

void DFS1(int u, int father) {
	dep[u] = dep[father] + 1, sz[u] = 1, fa[u] = father;

	for(const int& v : e[u]) {
		if(v == father) continue;
		DFS1(v, u);
		sz[u] += sz[v];
		if(sz[son[u]] < sz[v]) son[u] = v;  
	}
}

void DFS2(int u, int t) {
	top[u] = t;
	if(!son[u]) return ;
	DFS2(son[u], t);
	for(const int& v : e[u]) if(v != fa[u] && v != son[u]) DFS2(v, v);
}

inline int LCA(int u, int v) {
	while(top[u] != top[v]) {
		if(dep[top[u]] < dep[top[v]]) std::swap(u, v);
		u = fa[top[u]];
	}
	return dep[u] < dep[v] ? u : v;
}

int Findest(int x) {
	return x == F[x] ? x : F[x] = Findest(F[x]);
}

inline void KruskalBuildTree() {
	std::sort(adj.begin(), adj.end(), std::greater<edge>());
	Rep(i, 1, n + 1) F[i] = i;
	int tot = n;

	rep(i, adj.size()) {
		int u = adj[i].u, v = adj[i].v, w = adj[i].w;
		int fu = Findest(u), fv = Findest(v);
		if(fu == fv) continue;
		
		cost[++tot] = w;
		F[tot] = F[fu] = F[fv] = tot;

		e[tot].push_back(fu);
		e[tot].push_back(fv);
		e[fu].push_back(tot);
		e[fv].push_back(tot);
	}

	Rep(i, 1, tot + 1) if(F[i] == i) DFS1(i, 0), DFS2(i, i);
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    
    Init();

    rep(i, m) {
    	int a, b, c;
    	std::cin >> a >> b >> c;
    	adj.push_back({a, b, c});
    }
    
    KruskalBuildTree();

    std::cin >> q;
    while(q--) {
    	int x, y;
    	std::cin >> x >> y;
    	std::cout << cost[LCA(x, y)] << '\n';
    }

    
    
    return 0;
}

Tarjan(scc):

int n, m, tot = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> low, dfn, id, sz;
std::vector<bool> in_stk;
std::stack<int> stk;

void Tarjan(int u) {
	low[u] = dfn[u] = ++tot;
	stk.push(u), in_stk[u] = true;

	for(const int& v : e[u]) {
		if(!dfn[v]) {
			Tarjan(v);
			low[u] = std::min(low[u], low[v]);
		}else if(in_stk[v]) low[u] = std::min(low[u], dfn[v]);
	}

	if(dfn[u] == low[u]) {
		int v;
		cnt++;
		do{
			v = stk.top();
			stk.pop();
			in_stk[v] = false;
			id[v] = cnt;
			sz[cnt]++;
		}while(v != u);
	}
}

Tarjan(scc割边):

void Tarjan(int u, int father) {
    low[u] = dfn[u] = ++tot;
    
    for(const int& v : e[u]) {
        if(v == father) continue;
        if(!dfn[v]) {
            Tarjan(v, u);
            low[u] = std::min(low[u], low[v]);
            if(low[v] > dfn[u]) output.push_back({u, v});
        }else low[u] = std::min(low[u], dfn[v]);
    }
}

Tarjan(scc割点):

inline void Tarjan(int u) {
	low[u] = dfn[u] = ++cnt;
	int child = 0;
	for(const int& v : e[u]) {
		if(!dfn[v]) {
			Tarjan(v);
			low[u] = std::min(low[u], low[v]);
			 if(low[v] >= dfn[u]) {
			 	child++;
			 	if(child > 1 || u != root) cut[u] = true;
			 }
		}else low[u] = std::min(low[u], dfn[v]);
	}
}

Tarjan(edcc):

int n, m, tot = 0, cnt = 0;
std::vector<edge> e;
std::vector<int> low, dfn, h, bri, dcc, d ;
std::stack<int> stk;

inline void add(int a, int b) {
	e.push_back({b, h[a]});
	h[a] = e.size() - 1;
}

void Tarjan(int u, int in_edge) {
	low[u] = dfn[u] = ++tot;
	stk.push(u);

	for(int i = h[u]; ~i; i = e[i].ne) {
		int v = e[i].v;
		if(!dfn[v]) {
			Tarjan(v, i);
			low[u] = std::min(low[u], low[v]);
			if(low[v] > dfn[u]) bri[i] = bri[i ^ 1] = true;
		}else if(i != (in_edge ^ 1)) low[u] = std::min(low[u], dfn[v]);
	}

	if(dfn[u] == low[u]) {
		int v;
		cnt++;
		do{
			v = stk.top();
			stk.pop();
			dcc[v] = cnt;
		}while(v != u);
	}
}

Tarjan(vdcc):

int n, m, tot = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> low, dfn, dcc;
std::stack<int> stk;

inline void Tarjan(int u) {
	low[u] = dfn[u] = ++cnt;
	if(e[u].empty()) {
		dcc[++cnt].push_back(u);
		return ;
	}
	int child = 0;
	for(const int& v : e[u]) {
		if(!dfn[v]) {
			Tarjan(v);
			low[u] = std::min(low[u], low[v]);
			 if(low[v] >= dfn[u]) {
			 	child++;
			 	if(child > 1 || u != root) cut[u] = true;
			 	int ne;
			 	cnt++;
			 	do{
			 		ne = stk.top();
			 		stk.pop();
			 		dcc[cnt].push_back(ne);
			 	}while(ne != u);
			 }
		}else low[u] = std::min(low[u], dfn[v]);
	}
}

main(建图)//给每个割点一个新编号(cnt+1开始)
num = cnt;
Rep(i, 1, n + 1) if(cut[i]) id[i] = ++num;
//建新图,从每个vDCC向对应割点连边
Rep(u, 1, cnt + 1){
    for(const int& v : e[u]){
        if(cut[v]){
            ne[i].push_back(id[v]);
            ne[id[v]].push_back(i);
        }
    }
}

2-SAT:

int n, m, tot = 0, cnt = 0;
std::vector<std::vector<int>> e;
std::vector<int> low, dfn, id, sz;
std::vector<bool> in_stk;
std::stack<int> stk;

void Tarjan(int u) {
    low[u] = dfn[u] = ++tot;
    stk.push(u), in_stk[u] = true;

    for(const int& v : e[u]) {
        if(!dfn[v]) {
            Tarjan(v);
            low[u] = std::min(low[u], low[v]);
        } else if(in_stk[v]) low[u] = std::min(low[u], dfn[v]);
    }

    if(dfn[u] == low[u]) {
        int v;
        cnt++;
        do{
            v = stk.top();
            stk.pop();
            in_stk[v] = false;
            id[v] = cnt;  // 记录SCC编号
            sz[cnt]++;
        }while(v != u);
    }
}

// 添加约束:x取a或y取b(a,b∈{0,1})
// 注意:变量x的编号应为0到n-1
void add_clause(int x, int a, int y, int b) {
    // 变量x的真值为2x,假值为2x+1
    int u = 2 * x + a;
    int v = 2 * y + b;
    
    e[u ^ 1].push_back(v);   // ¬u → v
    e[v ^ 1].push_back(u);   // ¬v → u
}

bool solve_2sat() {
    // 初始化数组
    dfn.resize(2 * n + 2, 0);
    low.resize(2 * n + 2, 0);
    id.resize(2 * n + 2, 0);
    sz.resize(2 * n + 2, 0);
    in_stk.resize(2 * n + 2, false);
    e.resize(2 * n + 2);
    
    rep(i, 2 * n) if (!dfn[i]) Tarjan(i);
    
    
    // 检查每个变量的两个状态是否在同一SCC中
    rep(i, n) if (id[2 * i] == id[2 * i+1]) return false;
     
    return true;
}

欧拉回路(有向图):

1.对于无向图
(1)存在欧拉路径的充分必要条件:度数为奇数的点只能有0或2个。
(2)存在欧拉回路的充分必要条件:度数为奇数的点只能有0个。

2.对于有向图,所有边都是连通。
(1)存在欧拉路径的充分必要条件:要么所有点的出度均等于入度;要么除了两个
点之外,其余所有点的出度等于入度,剩余的两个点:一个满足出度比入度多1(起
点),另一个满足入度比出度多1(终点)。
(2)存在欧拉回路的充分必要条件:所有点的出度均等于入度。

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <algorithm>
#include <stack>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();

int n, m, s = 1;
std::vector<std::vector<int>> e;
std::vector<int> din, dout, del;
std::stack<int> stk;

void DFS(int u) {
	for(int i = del[u]; i < e[u].size(); i = del[u]) {
		del[u] = i + 1;
		DFS(e[u][i]);
	}
	stk.push(u);
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);
    
    std::cin >> n >> m;
    e.resize(n + 1);
    din.resize(n + 1);
    dout.resize(n + 1);
    del.resize(n + 1, 0);

    rep(i, m) {
    	int a, b;
    	std::cin >> a >> b;
    	e[a].push_back(b);
    	din[b]++, dout[a]++;
    }
    
    Rep(i, 1, n + 1) std::sort(e[i].begin(), e[i].end());

    int cnt_out = 0, cnt_in = 0;
    bool flag = false;
    Rep(i, 1, n + 1) {
    	if(din[i] != dout[i]) { // 有入度出度不相等的点 
    		flag = true;
    		int delta = dout[i] - din[i]; // 出度 - 入度
    		if(delta == 1) cnt_out++, s = i; // 起点
    		else if(delta == -1) cnt_in++;
    		else {
    			std::cout << "No\n";
    			return 0;
    		}
    	}
    }

    if(flag && cnt_in != cnt_out && cnt_out != 1) {
    	std::cout << "No\n";
    	return 0;
    }

    DFS(s);

    while(!stk.empty()) std::cout << stk.top() << " ", stk.pop();
    
    return 0;
}

欧拉回路(无向图):

#include <iostream>
#include <climits>
#include <limits>
#include <vector>
#include <stack>
#include <unordered_map>
#include <algorithm>

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;
typedef std::pair<int, int> PII;

#define rep(i, n) for(int i = 0; i < n; i++)
#define Rep(i, len, n) for(int i = len; i < n; i++)
#define MAX_INT 0x7fffffff
#define MIN_INT 0x80000000

const int INF = std::numeric_limits<int>::max();

int m;
std::vector<std::vector<int>> e;
//std::vector<std::vector<bool>> vis;
std::vector<int> odd, d;
std::unordered_map<int, std::unordered_map<int, int>> mp;
std::stack<int> stk;

void DFS(int u) {
    /*
    // 如果两点间只能一条路径经过一次
    while(!e[u].empty()) {
        int v = e[u].back();
        e[u].pop_back();
        if(vis[u][v] || vis[v][u]) continue;
        vis[u][v] = vis[v][u] = true;
        DFS(v);
    }
    */
    
    // 如果两点有多条路径,并且每条路径都能经过一次
    for(const int& v : e[u]) {
        if(mp[u][v]) {
            mp[u][v]--;
            mp[v][u]--;
            DFS(v);
        }
    }

    stk.push(u);
}

int main(void) {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr), std::cout.tie(nullptr);

    std::cin >> m;
    e.resize(500 + 1);
    d.resize(500 + 1);
    //vis.resize(500 + 1, std::vector<bool>(500 + 1, false));
    //mp.resize(500 + 1, std::vector<int>(500 + 1, 0));

    rep(i, m) {
        int a, b;
        std::cin >> a >> b;
        e[a].push_back(b);
        e[b].push_back(a);

        mp[a][b]++, mp[b][a]++;
        d[a]++, d[b]++;
    }

    Rep(i, 1, 500 + 1) if(!e[i].empty()) std::sort(e[i].begin(), e[i].end());

    Rep(i, 1, 500 + 1) if(!e[i].empty() && d[i] & 1) odd.push_back(i); // 不是孤点,并且是奇数
    
    int s = INF;
    if(odd.size() == 2) s = std::min(odd[0], odd[1]);
    else {
        Rep(i, 1, 500 + 1) {
            if(d[i]) {
                s = i;
                break;
            }
        }
    }
    
    if(s == INF) s = 1;
    
    DFS(s);

    while(!stk.empty()) std::cout << stk.top() << '\n', stk.pop();

    return 0;
}

树的直径:

int max_v = 0;
std::vector<std::vector<PII>> e;
std::vector<int> dist, from, a;

void DFS(int u, int fa) {
	from[u] = fa; // 记录直径路径上的点
	for(const auto& [v, w] : e[u]) {
		if(v == fa) continue;
		dist[v] = dist[u] + w; // 记录距离
		if(dist[v] > dist[max_v]) max_v = v; // 更新于起点最远的点
		DFS(v, u);
	}
}

inline void Build(int u) {
	while(u) { // 获取路径(反向)
		a。push_back(u);
		u = from[u];
	}
	std::reverse(a.begin(), a.end()); // 变成从起点到终点
}

inline void TreeL() {
	DFS(1, 0);
	dist[max_v] = 0;
	DFS(max_v, 0);
	Build(max_v);
}

树的重心:

树的中心:
树中所有节点的最长最短路径的中点(即树的直径的中点)
应用场景:选择一个点,使得最远节点的距离最小(如紧急集合点问题)。

树的重心:
树中所有节点到该点的距离之和最小的点
应用场景:选择一个点,使得所有节点的距离总和最小
int n;
std::vector<std::vector<int>> e;
std::vector<int> sz, dist, d;
std::vector<bool> vis;

int DFS(int u){
    vis[u] = true; //标记u这个点被搜过
    int sz = 0; //记录u的最大子树的结点数
    int sum = 1;  //记录以u为根的子树的结点数
    for(const int& v : e[u]){ 
        if(vis[v]) continue; //避免向上查找
        int s = DFS(v); //s是以v为根的子树的结点数
        sz = std::max(sz,s); //记录u的最大子树的结点数
        sum += s; //累加u的各个子树的结点数
    }
    ans = min(ans, max(sz, n - sum)); //去掉重心每个区间最大的节点数的最小值
    return sum; //返回以u为根的子树的结点数
}

树的中心:

 int n, sum = 0;
 std::vector<std::vector<int>> e;
 std::vector<int> d1, d2, up, p;
 std::vector<bool> vis;

 // 向下找找到最长路径和次长路径
 int DFSDown(int u, int fa) {
     d1[u] = d2[u] = 0;

     for(const int& v : e[u]) {
         if(v == fa) continue;
         int d = DFSDown(v, u) + 1;
         if(d >= d1[u]) {
             d2[u] = d1[u];
             d1[u] = d;
             p[u] = v; // 记录u的子节点是谁
         }else if(d > d2[u]) d2[u] = d;
     }

     return d1[u];
 }

 // 向上查找,如果说 v 位于最长路,则比较次长路和向上走那个距离更远,更新up[v]
 // 如果说 v 位于次长路,则比较最长路和向上走那个距离更远,更新up[v]
 void DFSUp(int u, int fa) {
     for(const int& v : e[u]) {
         if(v == fa) continue;
         if(p[u] == v) up[v] = std::max(up[u], d2[u]) + 1;
         else up[v] = std::max(up[u], d1[u]) + 1;
         DFSUp(v, u);
     }
 }

main(输出树的中心)DFSDown(任意点x, -1);
DFSUp(任意点x, -1);

int res = INF;
Rep(i, 1, n + 1) res = std::min(res, std::max(up[i], d1[i]));
std::cout << res << '\n';

树上操作整合:

const int INF = std::numeric_limits<int>::max();

int n, sum = 0, max_v = 0, ans = INF;
std::vector<std::vector<PII>> e;
std::vector<int> sz, dist, d, d1, d2, up, p, from, a;
std::vector<bool> vis;

// 树的直径
void DFS(int u, int fa) {
	from[u] = fa; // 记录直径路径上的点

	for (const auto& [v, w] : e[u]) {
		if (v == fa) continue;

		dist[v] = dist[u] + w; // 记录距离
		if (dist[v] > dist[max_v]) max_v = v; // 更新于起点最远的点
		DFS(v, u);
	}
}

// 树的重心(重载)
int DFS(int u) {
    vis[u] = true; // 标记u这个点被搜过
    sz[u] = 1; // 初始化以u为根的子树的结点数
    int max_sz = 0;  // 记录u的最大子树的结点数

    for (const auto& [v, w] : e[u]) {
        if (vis[v]) continue; // 避免向上查找
        int s = DFS(v); // s是以v为根的子树的结点数

        sz[u] += s; // 累加u的各个子树的结点数
        max_sz = std::max(max_sz, s); // 记录u的最大子树的结点数
    }

    ans = std::min(ans, std::max(max_sz, n - sz[u])); // 计算去掉u后最大子树的节点数
    return sz[u]; // 返回以u为根的子树的结点数
}

// 向下找找到最长路径和次长路径
int DFSDown(int u, int fa) {
	d1[u] = d2[u] = 0;

	for (const auto& [v, w] : e[u]) {
		if (v == fa) continue;
		int d = DFSDown(v, u) + 1;
		if (d >= d1[u]) {
			d2[u] = d1[u];
			d1[u] = d;
			p[u] = v; // 记录u的子节点是谁
		}
		else if (d > d2[u]) d2[u] = d;
	}

	return d1[u];
}

// 向上查找,如果说 v 位于最长路,则比较次长路和向上走那个距离更远,更新up[v]
// 如果说 v 位于次长路,则比较最长路和向上走那个距离更远,更新up[v]
void DFSUp(int u, int fa) {
	for (const auto& [v, w] : e[u]) {
		if (v == fa) continue;

		if (p[u] == v) up[v] = std::max(up[u], d2[u]) + 1;
		else up[v] = std::max(up[u], d1[u]) + 1;

		DFSUp(v, u);
	}
}

inline void Build(int u) {
	while (u) { // 获取路径(反向)
		a.push_back(u);
		u = from[u];
	}
	std::reverse(a.begin(), a.end()); // 变成从起点到终点
}

inline void TreeL() { // 获得直径
	DFS(1, 0);
	dist[max_v] = 0;
	DFS(max_v, 0);
	Build(max_v);
}

inline int TreeMid(int x) { // 获得中心
    DFSDown(x, 0);
    up[1] = 0;
    DFSUp(x, 0);

    int res = INF;
    Rep(i, 1, n + 1) res = std::min(res, std::max(up[i], d1[i]));
    std::cout << res << '\n';

    return res;
}

inline int Centroid(int x) { // 获得返回以x为根的子树的重心
	std::fill(vis.begin(), vis.end(), false);
	ans = INF;
    int s = DFS(x); // 重载的DFS计算重心(s是以x为根的子树的结点数)
    
    // 找到最小的最大子树节点数对应的节点
    int centroid = -1;
    Rep(i, 1, n + 1) {
        if (!vis[i]) continue; // 只考虑子树中的节点
        int max_sub = std::max(sz[i], s - sz[i]); // s是子树大小
        if (max_sub == ans) {
            centroid = i;
            break;
        }
    }
    return centroid;
}

inline void Init(int n) {
	e.resize(n + 1);
	sz.resize(n + 1);
	dist.resize(n + 1);
	d.resize(n + 1);
	d1.resize(n + 1);
	d2.resize(n + 1);
	up.resize(n + 1);
	p.resize(n + 1);
	from.resize(n + 1);
	vis.resize(n + 1, false);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2378408.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NBA足球赛事直播源码体育直播M33模板赛事源码

源码名称&#xff1a;体育直播赛事扁平自适应M33直播模板源码 开发环境&#xff1a;帝国cms7.5 空间支持&#xff1a;phpmysql 带软件采集&#xff0c;可以挂着自动采集发布&#xff0c;无需人工操作&#xff01; 演示地址&#xff1a;NBA足球赛事直播源码体育直播M33模板赛事…

电子电器架构 --- 整车造车阶段四个重要节点

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

黑马点评-用户登录

文章目录 用户登录发送短信验证码注册/登录校验登录 用户登录 发送短信验证码 public Result sendCode(String phone, HttpSession session) {// 1.校验手机号if (RegexUtils.isPhoneInvalid(phone)) {// 2.如果不符合&#xff0c;返回错误信息return Result.fail("手机…

十二、Hive 函数

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月1日 专栏&#xff1a;Hive教程 在数据处理的广阔天地中&#xff0c;我们常常需要对数据进行转换、计算、清洗或提取特定信息。Hive 提供了强大的内置运算符和丰富的内置函数库&#xff0c;它们就像魔法师手中的魔法棒&…

No More Adam: 新型优化器SGD_SaI

一.核心思想和创新点 2024年12月提出的SGD-SaI算法&#xff08;Stochastic Gradient Descent with Scaling at Initialization&#xff09;本质上是一种在训练初始阶段对不同参数块&#xff08;parameter block&#xff09;基于**梯度信噪比&#xff08;g-SNR, Gradient Signa…

JSP链接MySQL8.0(Eclipse+Tomcat9.0+MySQL8.0)

所用环境 Eclipse Tomcat9.0 MySQL8.0.21(下载&#xff1a;MySQL Community Server 8.0.21 官方镜像源下载 | Renwole&#xff09; mysql-connector-java-8.0.21&#xff08;下载&#xff1a;MySQL :: Begin Your Download&#xff09; .NET Framework 4.5.2&#xff08;下…

SEO长尾词与关键词优化实战

内容概要 在SEO优化体系中&#xff0c;长尾关键词与核心关键词的协同作用直接影响流量获取效率与用户转化路径。长尾词通常由3-5个词组构成&#xff0c;搜索量较低但意图明确&#xff0c;能精准触达细分需求用户&#xff1b;核心关键词则具备高搜索量与广泛覆盖能力&#xff0…

机器学习-人与机器生数据的区分模型测试-数据处理1

附件为训练数据&#xff0c;总体的流程可以作为参考。 导入依赖 import pandas as pd import os import numpy as np from sklearn.model_selection import train_test_split,GridSearchCV from sklearn.ensemble import RandomForestClassifier,VotingClassifier from skle…

HelloWorld

HelloWorld 新建一个java文件 文件后缀名为 .javahello.java【注意】系统可能没有显示文件后缀名&#xff0c;我们需要手动打开 编写代码 public class hello {public static void main(String[] args) {System.out.print(Hello,World)} }编译 javac java文件&#xff0c;会生…

SEO 优化实战:ZKmall模板商城的 B2C商城的 URL 重构与结构化数据

在搜索引擎算法日益复杂的今天&#xff0c;B2C商城想要在海量信息中脱颖而出&#xff0c;仅靠优质商品和营销活动远远不够。ZKmall模板商城以实战为导向&#xff0c;通过URL 重构与结构化数据优化两大核心策略&#xff0c;帮助 B2C 商城实现从底层架构到搜索展示的全面升级&…

数字万用表与指针万用表使用方法及注意事项

在电子测量领域&#xff0c;万用表是极为常用的工具&#xff0c;数字万用表和指针万用表各具特点。熟练掌握它们的使用方法与注意事项&#xff0c;能确保测量的准确性与安全性。下面为您详细介绍&#xff1a; 一 、数字万用表按钮功能 > 进入及退出手动量程模式 每 按 […

【读代码】端到端多模态语言模型Ultravox深度解析

一、项目基本介绍 Ultravox是由Fixie AI团队开发的开源多模态大语言模型,专注于实现音频-文本的端到端实时交互。项目基于Llama 3、Mistral等开源模型,通过创新的跨模态投影架构,绕过了传统语音识别(ASR)的中间步骤,可直接将音频特征映射到语言模型的高维空间。 核心优…

RabbitMQ工作流程及使用方法

一、什么是RabbitMQ RabbitMQ 是一款基于 ‌AMQP&#xff08;高级&#xff0c;消息队列协议&#xff09;‌ 的开源消息中间件&#xff0c;专为分布式系统设计&#xff0c;用于实现应用程序间的异步通信&#xff0c;其核心功能是通过 ‌消息代理&#xff08;Message Broker&…

算法:分治法

实验内容 在一个2kⅹ2k个方格组成的棋盘中&#xff0c;若恰有一个方格与其他方格不同&#xff0c;则称该方格为特殊方格&#xff0c;且称该棋盘为一特殊棋盘。 显然&#xff0c;特殊方格出现的位置有4k 种情况&#xff0c;即k>0,有4k 种不同的特殊棋盘 棋盘覆盖&#xff1a…

MySQL初阶:sql事务和索引

索引&#xff08;index&#xff09; 可以类似理解为一本书的目录&#xff0c;一个表可以有多个索引。 索引的意义和代价 在MySQL中使用select进行查询时会经过&#xff1a; 1.先遍历表 2.将条件带入每行记录中进行判断&#xff0c;看是否符合 3.不符合就跳过 但当表中的…

docker部署第一个Go项目

1.前期准备 目录结构 main.go package mainimport ("fmt""github.com/gin-gonic/gin""net/http" )func main() {fmt.Println("\n .::::.\n .::::::::.\n :::::::::::\n …

Visual Studio2022跨平台Avalonia开发搭建

由于我已经下载并安装了 VS2022版本&#xff0c;这里就跳过不做阐述。 1.安装 Visual Studio 2022 安装时工作负荷Tab页勾选 ‌“.NET 桌面开发”‌ 和“Visual Studio扩展开发”‌ &#xff0c;这里由于不是用的微软的MAUI&#xff0c;所以不用选择其他的来支持跨平台开发&a…

css iconfont图标样式修改,js 点击后更改样式

背景&#xff1a; 在vue项目中&#xff0c;通过点击/鼠标覆盖&#xff0c;更改选中元素的样式&#xff0c;可以通过js逻辑&#xff0c;也可以根据css样式修改。包括以下内容&#xff1a;iconfont图标的引入以及使用&#xff0c;iconfont图标样式修改【导入文件是纯白&#xff0…

开源项目实战学习之YOLO11:12.4 ultralytics-models-sam-memory_attention.py源码分析

👉 点击关注不迷路 👉 点击关注不迷路 👉 另外,前些天发现了一个巨牛的AI人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。感兴趣的可以点击相关跳转链接。 点击跳转到网站。 ultralytics-models-sam 1.sam-modules-memory_attention.pyblocks.py: 定义模…

【沉浸式求职学习day42】【算法题:滑动窗口】

沉浸式求职学习 长度最小的子数组水果成篮 关于算法题&#xff1a;滑动窗口的几个题目 长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 s &#xff0c;找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组&#xff0c;并返回其长度。如果不存在符合条件的子数组…