文章目录
2. 图论
2.1 Tarjan
2.1.1 割点
int n, m;
int head[maxn], cnt, dfn[maxn], low[maxn];
struct node {
int next, to;
} e[maxn * 2];
bool vis[maxn], cut[maxn];
void ins(int u, int v) {
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void cut_bridge(int cur, int fa, int dep) {
vis[cur] = 1;
dfn[cur] = low[cur] = dep;
int children = 0;
for (int i = head[cur]; i; i = e[i].next) {
int x = e[i].to;
if (x != fa && vis[x] == 1) {
if (dfn[x] < low[cur])low[cur] = dfn[x];
}
if (vis[x] == 0) {
cut_bridge(x, cur, dep + 1);
children++;
if (low[x] < low[cur])low[cur] = low[x];
if ((fa == -1 && children > 1) || (fa != -1 && low[x] >= dfn[cur]))cut[cur] = 1;
}
}
vis[cur] = 2;
}
2.1.2 边双联通分量
const int maxn = 1e4 + 10;
int n, m;
struct data {
int to, next;
} e[maxn * 10];
int head[maxn], cnt = 0;
int cur = 0, top = 0;
int low[maxn], dfn[maxn], vis[maxn], ans[maxn * 10];
void ins(int u, int v) {
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void tarjan(int u, int fa) {
vis[u] = 1;
dfn[u] = low[u] = ++cur;
for (int i = head[u]; i; i = e[i].next) {
int x = e[i].to;
if (vis[x] == 1 && x != fa) {
if (low[u] > dfn[x])low[u] = dfn[x];
ans[++top] = u;
ans[++top] = x;
}
if (vis[x] == 0) {
tarjan(x, u);
if (low[x] < low[u])low[u] = low[x];
if (low[x] > dfn[u]) {
ans[++top] = u;
ans[++top] = x;
ans[++top] = x;
ans[++top] = u;
} else {
ans[++top] = u;
ans[++top] = x;
}
}
}
vis[u] = 2;
}
//tarjan(i ,-1);
2.1.3 点双连通分量
const int maxn = 5010;
int n, m, q, head[maxn];
struct data {
int to, next;
} e[maxn * 4];
int cnt = 0, top = 0, tot = 0;
int dfn[maxn], low[maxn], s[maxn], vis[maxn];
vector<int> color[maxn];
int block[maxn];
int block_color = 0;
void ins(int u, int v) {
e[++cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt;
}
void bic(int v, int fa, int dep) {
vis[v] = 1;
s[++top] = v;
block[v] = block_color;
dfn[v] = low[v] = dep;
for (int i = head[v]; i; i = e[i].next) {
int x = e[i].to;
if (vis[x] == 0) {
bic(x, v, dep + 1);
if (low[x] >= dfn[v]) {
tot++;
do {
color[s[top]].push_back(tot);
top--;
} while (s[top + 1] != x);
if (low[x] == dfn[v])color[v].push_back(tot);
}
if (low[x] <= low[v])low[v] = low[x];
} else if (x != fa && vis[x] == 1)low[v] = min(low[v], dfn[x]);
}
vis[v] = 2;
}
//bic(x, -1, 0);
2.1.4 极大强连通分量
const int maxn = 1e4 + 10;
int n, m;
int cur = 0;
vector<int> v[maxn];
bool vis[maxn];
int low[maxn], dfn[maxn];
int s[maxn];
int top = 0, tot = 0;
void tarjan(int x) {
s[++top] = x;
vis[x] = 1;
dfn[x] = low[x] = ++cur;
for (int i = 0; i < v[x].size(); ++i) {
int u = v[x][i];
if (dfn[u] == 0)tarjan(u);
if (vis[u] && low[x] > low[u])low[x] = low[u];
}
if (low[x] == dfn[x]) {
tot = 0;
while (s[top + 1] != x) {
top--;
tot++;
}
}
}
2.2 路径
2.2.1 Dijkstra
使用堆优化Dijkstra,复杂度O(mlogn)。
struct heap {
int d, u;
bool operator<(const heap &b) const {
return d > b.d;
}
};
struct edge {
int to, next, vi;
} e[maxn * 2];
int head[maxn], cnt, dis[maxn];
void ins(int x, int y, int z) {
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].vi = z;
head[x] = cnt;
}
bool vis[maxn];
void dijkstra(int src) {
priority_queue<heap> q;
memset(dis, 63, sizeof dis);
memset(vis, 0, sizeof vis);
dis[src] = 0;
q.push((heap) {0, src});
while (!q.empty()) {
heap uu = q.top();
q.pop();
int x = uu.u;
if (vis[x])continue;
vis[x] = 1;
for (int i = head[x]; i; i = e[i].next) {
int v = e[i].to;
if (dis[v] > dis[x] + e[i].vi) {
dis[v] = dis[x] + e[i].vi;
q.push((heap{dis[v], v}));
}
}
}
}
邻接矩阵存储,复杂度O(n^2)。
int g[maxn][maxn], dis[maxn];
bool vis[maxn];
void dijkstra(int src) {
memset(vis, 0, sizeof vis);
memset(dis, 63, sizeof dis);
dis[src] = 0;
for (int i = 1; i <= n; ++i) {
int x = -1, m = inf;
for (int y = 1; y <= n; ++y) {
if (!vis[y] && dis[y] <= m) {
x = y;
m = dis[x];
}
}
if (x == -1)break;
vis[x] = 1;
for (int y = 1; y <= n; ++y) {
dis[y] = min(dis[y], dis[x] + g[x][y]);
}
}
}
2.2.2 SPFA
const int maxn = 1e5 + 10;
const int maxm = 5e5 + 10;
int n, m;
struct edge {
int to, next, vi;
} e[maxm];
int head[maxn], dis[maxn], cnt;
bool inq[maxn];
void ins(int x, int y, int z) {
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].vi = z;
head[x] = cnt;
}
queue<int> q;
void spfa(int src) {
memset(dis, 63, sizeof dis);
dis[src] = 0;
inq[src] = 1;
q.push(src);
while (!q.empty()) {
int l = q.front();
q.pop();
for (int i = head[l]; i; i = e[i].next) {
int x = e[i].to;
if (dis[l] + e[i].vi < dis[x]) {
dis[x] = dis[l] + e[i].vi;
if (!inq[x]) {
inq[x] = 1;
q.push(x);
}
}
}
inq[l] = 0;
}
}
2.2.3 Floyd - Warshall
/*Warshall求传递闭包*/
void Warshall() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = g[i][j] | (g[i][k] & g[k][j]);
}
void floyd() {
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
2.2.4 次短路
const int maxm = 1e5 + 10;
const int maxn = 5e3 + 10;
int head[maxn], cnt, n, m, dis[maxn], dis1[maxn];
typedef pair<int, int> P;
priority_queue<P, vector<P>, greater<P> > q;
struct edge {
int to, next, vi;
} e[maxm * 2];
void ins(int x, int y, int z) {
e[++cnt].to = y;
e[cnt].next = head[x];
e[cnt].vi = z;
head[x] = cnt;
}
int main() {
while (~scanf("%d%d", &n, &m)) {
cnt = 0;
memset(head, 0, sizeof(head));
memset(dis, 63, sizeof(dis));
memset(dis1, 63, sizeof(dis1));
int x, y, z;
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &x, &y, &z);
ins(x, y, z);
ins(y, x, z);
}
dis[1] = 0;
q.push(P(0, 1));
while (!q.empty()) {
P u = q.top();
q.pop();
int v = u.second;
for (int i = head[v]; i; i = e[i].next) {
if (dis[e[i].to] > u.first + e[i].vi) {
dis1[e[i].to] = dis[e[i].to];
dis[e[i].to] = u.first + e[i].vi;
q.push(P(dis[e[i].to], e[i].to));
} else if (dis1[e[i].to] > u.first + e[i].vi && dis[e[i].to] < u.first + e[i].vi) {
dis1[e[i].to] = u.first + e[i].vi;
q.push(P(dis1[e[i].to], e[i].to));
}
}
}
printf("%d\n", dis1[n]);
}
return 0;
}
2.3 匹配
2.3.1 匹配的相关概念
记图G=(V,E)。
1.匹配:(边集)在G中两两没有公共端点的边集合ME。
2.边覆盖:G中的任意顶点都至少是F中某条边的端点的边集合FE。
3.独立集:(点集)在G中两两互不相连的顶点集合SV。
4.顶点覆盖:G中的任意边都有至少一个端点属于S的顶点集合SV。
a.对于不存在孤立点的图,|最大匹配|+|最小边覆盖|=|V|。
b.|最大独立集|+|最小顶点覆盖|=|V|。
c.对于二分图:|最大匹配|=|最小顶点覆盖|。
2.3.2 朴素匹配
求取最大匹配。
bool g[maxn][maxn];
bool vis[maxn];
int p[maxn], n;
bool find_path(int x) {
for (int i = 1; i <= n; i++) {
if (!vis[i] && g[x][i]) {
vis[i] = 1;
if (!p[i] || find_path(p[i])) {
p[i] = x;
return 1;
}
}
}
return 0;
}
//if (find_path(i))tot++;
2.3.3 匈牙利算法
邻接矩阵存储。
int g[maxn][maxn];
int from[maxn];
bool vis[maxn];
bool match(int x) {
for (int i = 1; i <= n; ++i) {
if (g[x][i] && vis[i] == 0) {
vis[i] = 1;
if (from[i] == -1 || match(from[i])) {
from[i] = x;
return 1;
}
}
}
return 0;
}
int hungry() {
int tot = 0;
memset(from, -1, sizeof(from));
for (int i = 1; i <= n; ++i) {
memset(vis, 0, sizeof(vis));
if (match(i))
++tot;
}
return tot;
}
2.4 树
2.4.1 lca
const int maxn = 1e5 + 10;
const int maxh = 17;
int n, q, tot, head[maxn], f[maxn][25], dep[maxn];
struct Edge {
int to, next;
} e[2 * maxn];
void ins(int x, int y) {
e[++tot].to = y, e[tot].next = head[x], head[x] = tot;
}
void dfs(int x, int fa) {
f[x][0] = fa;
for (int i = 1; (1 << i) <= dep[x]; i++)
f[x][i] = f[f[x][i - 1]][i - 1];
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (y == fa) continue;
dep[y] = dep[x] + 1;
dfs(y, x);
}
}
int lca(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
for (int i = maxh; i >= 0; i--)
if (dep[f[y][i]] >= dep[x]) y = f[y][i];
if (x == y) return x;
for (int i = maxh; i >= 0; i--)
if (f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
2.4.2 生成树Kruskal
const int maxn = 2e5 + 10;
int r[maxn], p[maxn], u[maxn], v[maxn], w[maxn];
bool cmp(const int i, const int j) { return w[i] < w[j]; }
int find(int x) { return p[x] == x ? p[x] : p[x] = find(p[x]); }
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)scanf("%d%d%d", &v[i], &u[i], &w[i]);
for (int i = 1; i <= n; i++)p[i] = i;
for (int i = 1; i <= m; i++)r[i] = i;
sort(r + 1, r + 1 + m, cmp);
int tot = 0, sum = 0;
for (int i = 1; i <= m; i++) {
int e = r[i];
int x = find(v[e]);
int y = find(u[e]);
if (x != y) {
p[x] = y;
tot++;
sum += w[e];
}
}
return 0;
}
2.5 网络流
2.5.1 最大流 Ford-Fulkerson实现
struct edge {
int to, next, cap, rev;
} e[10010];
bool vis[1010];
int head[310], n, m, cnt = 0;
void ins(int s, int t, int c) {
e[++cnt].to = t;
e[cnt].next = head[s];
head[s] = cnt;
e[cnt].cap = c;
e[cnt].rev = cnt + 1;
e[++cnt].to = s;
e[cnt].next = head[t];
head[t] = cnt;
e[cnt].cap = 0;
e[cnt].rev = cnt - 1;
}
int dfs(int v, int t, int f) {
if (v == t)return f;
vis[v] = 1;
for (int i = head[v]; i; i = e[i].next) {
if (!vis[e[i].to] && e[i].cap > 0) {
int d = dfs(e[i].to, t, min(f, e[i].cap));
if (d > 0) {
e[i].cap -= d;
e[e[i].rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t) {
int flow = 0;
for (;;) {
memset(vis, 0, sizeof(vis));
int f = dfs(s, t, 1e9);
if (f == 0)return flow;
flow += f;
}
}
2.5.2 最大流Dinic实现
const int maxn = 2e3 + 10;
const int maxm = 5e3 + 10;
struct edge {
int to, next, cap, rev;
} e[maxm];
int n, m, cnt = 0, head[maxn], dis[maxn], iter[maxn];
queue<int> q;
void ins(int x, int y, int z) {
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
e[cnt].cap = z;
e[cnt].rev = cnt + 1;
e[++cnt].to = x;
e[cnt].next = head[y];
head[y] = cnt;
e[cnt].cap = 0;
e[cnt].rev = cnt - 1;
}
void bfs(int s) {
memset(dis, -1, sizeof(dis));
while (!q.empty())q.pop();
dis[s] = 0;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = e[i].next) {
if (e[i].cap && dis[e[i].to] < 0) {
q.push(e[i].to);
dis[e[i].to] = dis[u] + 1;
}
}
}
}
int dfs(int v, int t, int f) {
if (v == t)return f;
for (int i = (iter[v] ? iter[v] : head[v]); i; i = e[i].next, iter[v] = i) {
if (e[i].cap && dis[v] < dis[e[i].to]) {
int d = dfs(e[i].to, t, min(f, e[i].cap));
if (d > 0) {
e[i].cap -= d;
e[e[i].rev].cap += d;
return d;
}
}
}
return 0;
}
int max_flow(int s, int t) {
int flow = 0;
for (;;) {
bfs(s);
if (dis[t] < 0)return flow;
memset(iter, 0, sizeof(iter));
int f;
while ((f = dfs(s, t, 1e9)) > 0)
flow += f;
}
}
2.5.3 费用流
SPFA实现:
const int maxm = 1e5 + 10;
const int maxn = 1e4 + 10;
int n, m;
int cnt, head[maxn], dis[maxn], prevv[maxn], preve[maxn];
struct edge {
int to, next, cap, cost, rev;
} e[maxm];
void ins(int x, int y, int z, int c) {
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
e[cnt].cap = z;
e[cnt].rev = cnt + 1;
e[cnt].cost = c;
e[++cnt].to = x;
e[cnt].next = head[y];
head[y] = cnt;
e[cnt].cap = 0;
e[cnt].rev = cnt - 1;
e[cnt].cost = -c;
}
int min_cost_flow(int s, int t, int f) {
int ans = 0;
while (f > 0) {
memset(dis, 63, sizeof(dis));
queue<int> q;
q.push(s);
dis[s] = 0;
while (!q.empty()) {
int v = q.front();
q.pop();
if (dis[v] > 1e8)continue;
for (int i = head[v]; i; i = e[i].next) {
if (e[i].cap > 0 && dis[e[i].to] > dis[v] + e[i].cost) {
dis[e[i].to] = dis[v] + e[i].cost;
prevv[e[i].to] = v;
preve[e[i].to] = i;
q.push(e[i].to);
}
}
}
if (dis[t] > 1e8)
return -1;
int d = f;
for (int i = t; i != s; i = prevv[i]) {
d = min(d, e[preve[i]].cap);
}
f -= d;
ans += d * dis[t];
for (int i = t; i != s; i = prevv[i]) {
e[preve[i]].cap -= d;
e[e[preve[i]].rev].cap += d;
}
}
return ans;
}