树的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(∣V∣2) 对稠密图好
堆优化 : O ( ∣ E ∣ ∗ l o g ∣ V ∣ ) O(|E|*log|V|) O(∣E∣∗log∣V∣) 对稀疏图好
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(∣V∣⋅∣E∣)
//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(∣V∣3)
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(∣E∣∗log∣E∣+∣N∣∗A(∣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(∣V∣2)
用堆 O ( ∣ E ∣ + ∣ V ∣ ∗ l o g ∣ V ∣ ) O(|E|+|V|*log|V|) O(∣E∣+∣V∣∗log∣V∣) 对稀疏图较好
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) O(VE)
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]);
}