模板:图论有关

树的dfs序
inline void dfs(int x)
{
	a[++cnt] = x;
	v[x] = 1;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (v[y]) continue;
		dfs(y);
	}
	a[++cnt] = x;
}
树的重心
//在删除重心后,产生的子树中最大的一棵最小
inline void dfs(int x)
{
	v[x] = 1;
	size[x] = 1;
	int maxx = 0;
	for (int i = head[x]; i; i = e[i].next);
	{
		int y = e[i].to;
		if (v[y]) continue;
		dfs(y);
		size[x] += size[y];
		maxx = max(maxx, size[y]);
	}
	maxx = max(maxx, n - size[x]);
	if (maxx < ans)
	{
		ans = maxx;
		pos = x; //重心
	}
}
图的连通块划分
inline void dfs(int x)
{
	v[x] = cnt;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (v[y]) continue;
		dfs(y);
	}
}
inline void work()
{
	for (int i = 1; i <= n; i++)
	if (!v[i])
	{
	 	cnt++;
	 	dfs(i);
	 }
}
树与图的bfs
inline void bfs()
{
 	memset(d, 0, sizeof d);
 	queue <int> q;
 	q.push(1);
 	d[1] = 1;
 	while (q.size())
 	{
 		int x = q.front();
 		q.pop();
 		for (int i = head[x]; i; i = e[i].next)
 		{
 			int y = e[i].to;
 			if (d[y]) continue;
 			d[y] = d[x] + 1;
 			q.push(y);
 		}
 	}
 }
拓扑排序
inline void add(int x, int y)
{
	e[++tot].next = head[x];
	e[tot].to = y;
	head[x] = tot;
}
inline void topsort()
{
	queue <int> q;
	for (int i = 1; i <= n; i++)
	if (deg[i] == 0) q.push(i);
	while (q.size())
	{
		int x = q.front();
		q.pop();
		a[++cnt] = x;
		for (int i = head[x]; i; i = e[i].next)
		{
			int y = e[i].to;
			if (--deg[y] == 0) q.push(y);
		}
	}
}
inline void work()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y);
		deg[y]++;
	}
	topsort();
	for (int i = 1; i <= cnt; i++)
	printf("%d ", a[i]);
}
dijkstra

无负权

O ( ∣ V ∣ 2 ) O(|V|^{2}) O(V2) 对稠密图好

堆优化 : O ( ∣ E ∣ ∗ l o g ∣ V ∣ ) O(|E|*log|V|) O(ElogV) 对稀疏图好

priority_queue < pair <int, int> > q;
inline void dijkstra()
{
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[1] = 0;
	q.push(make_pair(0, 1));
	while (q.size())
	{
		int x = q.top().second;
		q.pop();
		if (v[x]) continue;
		v[x] = 1;
		for (int i = head[x]; i; i = e[i].next)
		{
			int y = e[i].to;
			if (d[y] > d[x] + e[i].w)
			{
				d[y] = d[x] + e[i].w;
				q.push(make_pair(-d[y], y));
			}
		}
	}
}
SPFA

有负权,但无负环

最坏情况下复杂度 O ( ∣ V ∣ ⋅ ∣ E ∣ ) O(|V|·|E|) O(VE)

//bfs
queue <int> q;
void spfa()
{
	memset(d, 10, sizeof d);
	memset(v, 0, sizeof v);
	d[1] = 0;
	v[1] = 1;
	q.push(1);
	while (q.size())
	{
		int x = q.front();
		q.pop();
		v[x] = 0;
		//判负环
		cnt[x]++;
		if (cnt[x] >= n + 1) 
		{
			flag = 1;
			return;
		}
		for (int i = head[x]; i; i = f[i].next)
		{
			int y = f[i].to;
			if (d[x] + f[i].w < d[y])
			{
				d[y] = d[x] + f[i].w;
				if (!v[y])
				{
					q.push(y);
					v[y] = 1;
				}
			}
		}
	} 
}

//dfs
#define inf 2147483600
const int N = 500002;
queue <int> q;
bool flag;
void spfa(int x)
{
   if (flag) return;
   v[x] = 1;
   for (int i = head[x]; i; i = e[i].next)
   {
          int y = e[i].to;
          if (d[y] > d[x] + e[i].w)
          {
                d[y] = d[x] + e[i].w;
                if (v[y] || flag) 
                {
                	flag = 1;
                	break;
                }
                spfa(y);
          }
   }
   v[x] = 0;
}
int t
int main()
{
    scanf("%d", &t);
    while (t--)
    {
         scanf("%d%d", &n, &m);
         for (int i = 1; i <= n; i++) 
         {
         	head[i] = v[i] = 0;
         	d[i] = 0;
         }
         tot = 0;
         flag = 0;
         for (int i = 1; i <= m; i++)
         {
             int x, y, z;
             scanf("%d%d%d", &x, &y, &z);
             add(x, y, z);
             if (z > 0) add(y, x, z);    
         }
         for (int i = 1; i <= n; i++)
         {
             spfa(i);
             if (flag) break;
         }
         if (!flag) cout << "N0" << endl;
         else cout << "YE5" << endl;
    }
    return 0;
}
Floyd

可以有负权,但无负环

O ( ∣ V ∣ 3 ) O(|V|^{3}) O(V3)

for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
Kruskal

O ( ∣ E ∣ ∗ l o g ∣ E ∣ + ∣ N ∣ ∗ A ( ∣ V ∣ ) ) O(|E|*log|E|+|N|*A(|V|)) O(ElogE+NA(V)) 对稀疏图较好

struct Xiao
{
	int x, y, z;
}e[N];
bool mycmp(Xiao a, Xiao b)
{
	return a.z < b.z;
}
int get(int x)
{
	return fa[x] == x ? x : fa[x] = get(fa[x]);
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
	sort(e + 1, e + 1 + m, mycmp);
	for (int i = 1; i <= n; i++)
	fa[i] = i;
	for (int i = 1; i <= m; i++)
	{
		int x = get(e[i].x);
		int y = get(e[i].y);
		if (x == y) continue;
		fa[x] = y;
		ans += e[i].z;
	}
	cout << ans << endl;
}
Prim

普通方法 O ( ∣ V ∣ 2 ) O(|V|^{2}) O(V2)

用堆 O ( ∣ E ∣ + ∣ V ∣ ∗ l o g ∣ V ∣ ) O(|E|+|V|*log|V|) O(E+VlogV) 对稀疏图较好

inline void prim()
{
	memset(d, 0x3f, sizeof d);
	memset(v, 0, sizeof v);
	d[1] = 0;
	for (int i = 1; i < n; i++)
	{
		int x = 0;
		for (int j = 1; j <= n; j++)
		{
			if (!v[j] && (x == 0 || d[j] < d[x])) x = j;
			v[x] = 1;
			for (int y = 1; y <= n; y++)
			if (!v[y]) d[y] = min(d[y], a[x][y]);
		}
	}
}
int main()
{
	cin >> n >> m;
	memset(a, 0x3f, sizeof a);
	for (int i = 1; i <= n; i++)
	a[i][i] = 0;
	for (int i = 1; i <= m; i++)
	{
		int x, y, z;
		scanf("%d%d%d", &x, &y, &z);
		a[y][x] = a[x][y] = min(a[x][y], z);
	}
	prim();
	for (int i = 2; i <= n; i++)
	ans += d[i];
	cout << ans << endl;
}
树的直径
//DP
inline void dp(int x)
{
	v[x] = 1;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (v[y]) continue;
		dp(y);
		ans = max(ans, d[x] + d[y] + e[i].w);
		d[x] = max(d[x], d[y] + e[i].w);
	}
}

//两次BFS
LCA

O ( ( n + q ) l o g n ) O((n+q)logn) O((n+q)logn)

//树上倍增
#include <bits/stdc++.h>
using namespace std;
const int N = 200000;
int first[N] = {};
int tot = 0, n, m, t, maxx = 0;
int d[N] = {}, a[N][100];
queue<int> q;
struct zyl
{
	int to, next;
}f[N * 3]; 
void add(int x, int y)
{
	f[++tot].to = y;
	f[tot].next = first[x];
	first[x] = tot;
}
void bfs()
{
	q.push(1);
	d[1] = 1;
	while(!q.empty())
	{
		int x = q.front();
		q.pop();
		for(int i = first[x]; i; i = f[i].next)
		{
			int y = f[i].to;
			if(d[y])
			continue;
			d[y] = d[x]+1;
			a[y][0] = x;
			for(int j = 1; j <= t; j++)
			a[y][j] = a[a[y][j - 1]][j - 1];
			q.push(y);
		}
	}
}
int lca(int x, int y)
{
	if (d[x] > d[y]) swap(x, y);
	for (int i = t; i >= 0; i--)
    if (d[a[y][i]] >= d[x]) y = a[y][i];
	if (x == y) return x;
	for (int i = t; i >= 0; i--)
	if (a[x][i] != a[y][i]) x = a[x][i], y = a[y][i];
	return a[x][0];
}
int main()
{
	scanf("%d%d", &n, &m);
	t=(int)(log(n)/log(2))+1;
	for (int i = 1; i < n; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y);
		add(y, x);
	}
	bfs();
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		int r = lca(x, y);
		printf("%d\n", r);
	}
	return 0;
}
判割边

O ( n + m ) O(n+m) O(n+m)

inline void tarjan(int x, int ed)
{
	dfn[x] = low[x] = ++num;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (!dfn[y])
		{
			tarjan(y, i);
			low[x] = min(low[x], low[y]);
			if (low[y] > dfn[x]) br[i] = br[i ^ 1] = true;
		}
		else if (i != (ed ^ 1)) low[x] = min(low[x], dfn[y]);
	}
}
int main()
{
	cin >> n >> m;
	tot = 1;
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= n; i++)
	if (!dfn[i]) tarjan(1, 0);
	for (int i = 2; i < tot; i += 2)
	if (br[i]) printf("%d %d\n", e[i ^ 1].to, e[i].to);
}
判割点

O ( n + m ) O(n+m) O(n+m)

inline void tarjan(int x)
{
	dfn[x] = low[x] = ++num;
	int flag = 0;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (!dfn[y])
		{
			tarjan(y);
			low[x] = min(low[x], low[y]);
			if (low[y] >= dfn[x])
			{
				flag++;
				if (x != root || flag > 1) cut[x] = true;
			}
		}
		else low[x] = min(low[x], dfn[y]);
	}
}
int main()
{
	cin >> n >> m;
	tot = 1;
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		if (x == y) continue;
		add(x, y);
		add(y, x);
	}
	for (int i = 1; i <= n; i++)
	if (!dfn[i]) 
	{
		root = i;
		tarjan(i);
	}
	for (int i = 1; i <= n; i++)
	if (cut[i]) printf("%d ", i);
}
欧拉路
inline void euler()
{
	st[++top] = 1;
	while (top > 0)
	{
		int x = st[top], i = head[x];
		while (i && v[i]) i = e[i].next;
		if (i)
		{
			st[++top] = e[i].to;
			v[i] = v[i ^ 1] = true;
			head[x] = e[i].next;
		}
		else 
		{
			top--;
			ans[++t] = x;
		}
	}
}
int main()
{
	cin >> n >> m;
	tot = 1;
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y);
		add(y, x);
	}
	euler();
	for (int i = t; i; i--)
	printf("%d\n", ans[i]);
}
强连通分量

O ( n ) O(n) O(n)

vector <int> scc[N];
inline void tarjan(int x)
{
	dfn[x] = low[x] = ++num;
	st[++top] = x;
	ins[x] = 1;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (!dfn[y])
		{
			tarjan(y);
			low[x] = min(low[x], low[y]);
		}
		else if (ins[y]) low[x] = min(low[x], dfn[y]);
	}
	if (dfn[x] == low[x])
	{
		cnt++;
		int y;
		do
		{
			y = st[top--];
			ins[y] = 0;
			c[y] = cnt;
			scc[cnt].push_back(y);
		} while (x !- y);
	}
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= m; i++)
	{
		int x, y;
		scanf("%d%d", &x, &y);
		add(x, y);
	}
	for (int i = 1; i <= n; i++)
	if (!dfn) tarjan(i);
	//缩点
	for (int x = 1; x <= n; x++)
	for (int i = head[x]; i ; i = e[i].next)
	{
		int y = e[i].to;
		if (c[x] == c[y]) continue;
		add_c(c[x], x[y]);
	}
}
二分图判定
inline void dfs(int x, int color)
{
	if (flag) return;
	v[x] = color;
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (v[y] == 0) dfs(y, 3 - color);
		else if (v[y] == color) 
		{
			flag = 1;
			return;
		}
	}
}
inline void work()
{
	flag = 0;
	for (int i = 1; i <= n; i++)
	if (!v[i]) dfs(i, 1);
}
二分图匹配

O ( V E ) O(VE) OVE

bool dfs(int x)
{
	for (int i = head[x]; i; i = e[i].next)
	{
		int y = e[i].to;
		if (v[y]) continue;
		v[y] = 1;
		if (!flag[y] || dfs(flag[y]))
		{
			flag[x] = y;
			flag[y] = x;
			return 1;
		}
	}
	return 0;
}
inline void work()
{
	for (int i = 1; i <= n; i++)
	if (!flag[i])
	{
		memset(v);
		if (dfs(i)) ans++;
	}
}

以下来自大佬博客(ps:手动转换格式)

%%% stO sto qyxpsx7 orz Orz %%%

https://blog.csdn.net/qyxpsx7/article/details/103044843#commentBox

Floyed求无向图最小环长度
//自己时,e[][]和dis[][]赋值0
//无边时,e[][]和dis[][]赋值+oo;
//有边时,e[][]和dis[][]赋值最小连边的值;
int ans = inf;
for (k = 1; k <= n; ++k) 
{
	for (i = 1; i < k; ++i)
	for (j = i+1; j < k; ++j)
	ans = min(ans, dis[i][j] + e[i][k] + e[k][j]);
	for (i = 1; i <= n; ++i)
	for (j = 1; j <= n; ++j)
	dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
Floyed求有向图最小环长度
//e[][]赋值+oo;
//有边时,e[][]赋值最小连边的值;
int ans = inf;
for (k = 1; k <= n; ++k)
for (i = 1; i <= n; ++i)
for (j = 1; j <= n; ++j)
e[i][j] = min(e[i][j], e[i][k] + e[k][j]);
for (i = 2; i <= n; ++i) 
ans = min(ans, e[i][i]);
Floyed求无向图最小环路径
//自己连时dis与e赋值为0,其余全部赋值+oo
//有边时赋值边权(重边选取最小值)
void floyd() 
{
    ans = inf;
    for (int k = 1; k <= n; k++)
    {
        for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
        if(i == j || k == i || k == j) continue;
        else if (dis[i][j] + e[i][k] + e[k][j] < ans)
        {
             ans = dis[i][j] + e[i][k] + e[k][j];
             num = 0;
             path[num++] = k;
             int temp = j;
             while (temp != i)
             {
                  path[num++] = temp;
                  temp = pre[i][temp];
             }
                  path[num++] = i;
        }
        for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++)
        if (i == j || k == i || k == j) continue;
        else if (dis[i][j] > dis[i][k] + dis[k][j])
        {
             dis[i][j] = dis[i][k] + dis[k][j];
             pre[i][j] = pre[k][j];
        }
    }
}
for (int i = 0; i < num; i++)
printf("%d ", path[i]);
拓扑排序
head = tail = 0;
for (int i = 1; i <= n; i++)
if (!deg[i]) q[++tail] = i;
while (head < tail)
{
	head++;
	for (int i = 1; i <= n; i++) 
	if (a[q[head]][i])
	{
		if (--deg[i] == 0) q[++tail] = i;
	}
}
对DAG求关键路径(邻接矩阵/邻接表)

(条件:拓扑排序为0,1,2,……)

//f[i]表示为事件i的最早开工时间
//g[i]表示为事件i的最晚开工时间
//顺拓扑排序取大值求出f数组、逆拓扑序列取小值求出g数组
//最后找出f[i] = g[i]的顶点,即关键路径上的顶点。
//f[i] = min(f[j] + G[i][j]);
//g[j] = min(g[j] - G[j][i]);
//j为i的前驱
//边界条件:f[0] = 0, g[n] = f[n-1];

f[0] = 0;
for (int i = 1; i <= n; i++) 
{
	int maxx = 0;
	for (j = 0; j <= n; j++) 
	if (G[j][i] != +oo)
	if (G[j][i] + f[j] > maxx) maxx = G[j][i] + f[j];
	f[i] = maxx;
}
g[n] = f[n];
for (int i = n - 1; i >= 0; i--) 
{
	int minn = +oo;
	for (j = 0; j <= n; j++) 
	if (G[j][i] != +oo)
	if (g[j] - G[j][i] < minn) minn = g[j] - G[j][i];
	g[i] = minn;
}
for (int i = 1; i < n; i++)
if (id(f[i] == g[i])) printf("%d->", i);
//ee[]、et[]表示活动的最早(晚)开始时间
printf("%d", n);
对拓扑排序求关键路径(邻接矩阵)
//f[i]表示为事件i的最早开工时间
//g[i]表示为事件i的最晚开工时间

void print(int i)
{
	if (i == -1) return;
	print(prev[i]);
	cout << i << " "; 
}
inline void work()
{
	memset(f, 0, sizeof f);
	memset(g, 0, sizeof g);
	memset(prev, -1, sizeof prev);
	for (int i = 1; i <= n; i++) 
	for (int j = 0; j < i; j++) 
	{
		int x = a[j], y = a[i];
		if(G[j][i] != +oo)
		if(G[x][y] + f[x] > f[y])
		{
			f[y] = G[x][y] + f[x];
			prev[y] = x;
		}
	}
	g[n] = f[n];
	for (int i = n - 1; i >= 0; i--) 
	for (int j = n; j > i; j--) 
	{
		int x = a[j], y = a[i];
		if(G[j][i] != +oo)
		if(g[x] - G[y][x] > g[y])
		g[y] = g[x] - G[y][x];
	}
	cout << "len=" << f[a[n]] << endl;
	print(a[n]);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值