2. 图论

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值