• LCA算法实现


    参考问题:洛谷P3379 【模板】最近公共祖先(LCA):https://www.luogu.com.cn/problem/P3379

    暴力解法,dfs一下,求得所有点的深度,然后每当我们要求 (x)(y) 的 LCA 的时候,我们就循环的去判断:

    • 如果当前 (x) 的深度大于 (y) 的深度,则令 (x) 变成 (x) 的父亲;
    • 否则,如果当前 (y) 的深度大于 (x) 的深度,则令 (y) 变成 (y) 的父亲;
    • 否则(说明当前 (x)(y) 处在同一深度),如果 (x e y),则令 (x) 变成 (x) 的父亲(或者令 (y) 变成 (y)的父亲,均可);
    • 否则(说明 (x=y)),返回 (x)(或者 (y),均可)。

    这种暴力的解法的时间复杂度达到了 (O(n cdot m)),实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 500050;
    int n, m, rt, pa[maxn], dep[maxn];
    vector<int> g[maxn];
    void dfs(int u, int d) {
        dep[u] = d;
        int sz = g[u].size();
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (v == pa[u]) continue;
            pa[v] = u;
            dfs(v, d+1);
        }
    }
    int query(int x, int y) {
        while (x != y) {
            if (dep[x] < dep[y]) y = pa[y];
            else x = pa[x];
        }
        return x;
    }
    int main() {
        cin >> n >> m >> rt;
        for (int i = 1; i < n; i ++) {
            int u, v;
            cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(rt, 1);
        while (m --) {
            int x, y;
            cin >> x >> y;
            cout << query(x, y) << endl;
        }
        return 0;
    }
    

    借助于倍增思想可以将上述算法优化到 (O(m cdot log n))
    下面的实现中:
    (pa[u][i]) 表示深度比 (u)(2^i) 的祖先节点的编号,如果这个节点不存在,则该值对应为 (rt)(根节点)。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 500050;
    int n, m, rt, pa[maxn][21], dep[maxn];
    vector<int> g[maxn];
    void dfs(int u, int p) {
        dep[u] = dep[p] + 1;
        pa[u][0] = p;
        for (int i = 1; (1<<i) <= dep[u]; i ++)
            pa[u][i] = pa[ pa[u][i-1] ][i-1];
        int sz = g[u].size();
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (v == p) continue;
            dfs(v, u);
        }
    }
    int lca(int x, int y) {
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 20; i >= 0; i --) {
            if (dep[ pa[x][i] ] >= dep[y]) x = pa[x][i];
            if (x == y) return x;
        }
        for (int i = 20; i >= 0; i --) {
            if (pa[x][i] != pa[y][i]) {
                x = pa[x][i];
                y = pa[y][i];
            }
        }
        return pa[x][0];
    }
    int main() {
        cin >> n >> m >> rt;
        for (int i = 1; i < n; i ++) {
            int u, v;
            cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs(rt, 0);
        while (m --) {
            int x, y;
            cin >> x >> y;
            cout << lca(x, y) << endl;
        }
        return 0;
    }
    

    然而不知为何在这道题仍然是70分超时。(可能是由于对数影响)

    Tarjan求LCA

    参考资料:https://riteme.site/blog/2016-2-1/lca.html

    注:写的非常好。

    实现代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 500050, maxm = maxn*2;
    struct Edge {
        int v, nxt;
        Edge() {};
        Edge(int _v, int _nxt) { v = _v; nxt = _nxt; }
    } edge[maxm];
    int n, m, rt, head[maxn], ecnt;
    int f[maxn];
    void init() {
        // 图部分
        memset(head, -1, sizeof(int)*(n+1));
        ecnt = 0;
        // 并查集部分
        for (int i = 1; i <= n; i ++) f[i] = i;
    }
    void addedge(int u, int v) {
        edge[ecnt] = Edge(v, head[u]); head[u] = ecnt ++;
        edge[ecnt] = Edge(u, head[v]); head[v] = ecnt ++;
    }
    struct Query {
        int id, v;  // id表示问题编号,v表示另一个点编号
        Query() {};
        Query(int _id, int _v) { id = _id; v = _v; }
    };
    vector<Query> query[maxn];
    void addquery(int x, int y, int id) {
        query[x].push_back(Query(id, y));
        query[y].push_back(Query(id, x));
    }
    int Find(int x) {
        return f[x] == x ? x : f[x] = Find(f[x]);
    }
    void Union(int x, int y) {  // 这里是将y集合所在的子树合并为x所在根节点的子树
        int a = Find(x), b = Find(y);
        if (a != b) f[b] = a;
    }
    int vis[maxn], ans[maxn];
    void tarjan(int u) {
        vis[u] = 1;
        for (int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if (vis[v]) continue;
            tarjan(v);
            f[v] = u;   // Union(u, v);
        }
        int sz = query[u].size();
        for (int i = 0; i < sz; i ++) {
            int id = query[u][i].id, v = query[u][i].v;
            if (vis[v] == 2) ans[id] = Find(v);
        }
        vis[u] = 2;
    }
    int main() {
        scanf("%d%d%d", &n, &m, &rt);
        init();
        for (int i = 1; i < n; i ++) {
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
        }
        for (int i = 1; i <= n; i ++) {
            int x, y;
            scanf("%d%d", &x, &y);
            if (x == y) ans[i] = x;
            else addquery(x, y, i);
        }
        tarjan(rt);
        for (int i = 1; i <= n; i ++) printf("%d
    ", ans[i]);
        return 0;
    }
    
  • 相关阅读:
    SpringBoot学习笔记(14)----应用监控-HTTP方式
    SpringBoot学习笔记(13)----使用Spring Session+redis实现一个简单的集群
    SpringBoot学习笔记(12)----SpringBoot实现多个 账号轮询发送邮件
    SpringBoot学习笔记(11)-----SpringBoot中使用rabbitmq,activemq消息队列和rest服务的调用
    SpringBoot学习笔记(8)-----SpringBoot文件上传
    SpringBoot学习笔记(7)-----CORS支持解决跨域问题
    设计模式:迭代器模式(Iterator)
    设计模式:适配器模式(Adapter)
    设计模式:状态模式(State)
    设计模式:抽象工厂模式(Abstract Factory)
  • 原文地址:https://www.cnblogs.com/quanjun/p/12927421.html
Copyright © 2020-2023  润新知