zoukankan      html  css  js  c++  java
  • 洛谷p1967货车运输(kruskal重构树)

    题面

    题解中有很多说最优解是kruskal重构树

    所以

    抽了个早自习看了看这方面的内容

    我看的博客

    感觉真的挺好使的

    首先对于kruskal算法来说

    是基于贪心的思想把边权排序用并查集维护是否是在同一棵树上

    对于kruskal重构树来说

    按不同边权顺序排序可相应的得到最大边权的最小值 、最小边权的最大值等问题

    建树过程:

    排好序后, 遍历, 若两条边u, v不在同一并查集内, 那么就新建一个节点, 这个节点的点权就代表u到v的边权, 同时将这三个点都加入同一并查集

    需要注意

    最后建立出来的可能是森林,也就是并不是所有的点的根都是相同的

    此时

    可以用一个vis数组来确保所有的点都被遍历过了

    1.若按照边权的升序排列

    那么最后可以求得最大边权的最小值

    感性理解一下

    因为最后是要求最小值

    所以

    建一棵最小生成树

    此时最大的边权就是所有别的建树方法中的最小值

    2.若按照边权的降序排列

    同上

    要求最小边权的最大值

    最后答案是最大值

    那么建一棵最大生成树

    关于为什么lca是答案:

    感性理解一下

    如果我们建的树是最小生成树

    那么

    显然边权越大深度越深

    我们是想按着边权尽量小的走

    lca(u, v)是原图u->v节点深度最小的点

    所以就是他了

    关于此题的代码:

    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 500010;
    int n, m, sum, fa[N], cnt, size[N], top[N], dad[N], head[N], va[N], tot, q, deep[N], vis[N], son[N];
    struct Edge{
        int from, to, w;
    }e[N << 1];
    struct Node {
        int nxt, to, w;
    }ee[N];
    void add(int x, int y) {
        ee[++tot].nxt = head[x];
        ee[tot].to = y;
        head[x] = tot;
    }
    bool cmp(Edge x, Edge y) {
        return x.w > y.w;
    }
    int find(int x) {
        return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    void dfs(int u, int pa) {
        size[u] = 1;vis[u] = 1;
        for(int i = head[u]; i; i = ee[i].nxt) {
            int v = ee[i].to;
            if(v == pa) continue;
            deep[v] = deep[u] + 1, dad[v] = u;
            dfs(v, u);
            size[u] += size[v];
            if(size[v] > size[son[u]]) son[u] = v;
        }
    }
    void dfs1(int u, int tp) {
        top[u] = tp;
        if(son[u]) dfs1(son[u], tp);
        for(int i = head[u]; i; i = ee[i].nxt) {
            int v = ee[i].to;
            if(v == dad[u] || v == son[u]) continue;
            dfs1(v, v);
        }
    }
    int lca(int x, int y) {
        while(top[x] != top[y]) {
            if(deep[top[x]] < deep[top[y]]) swap(x, y);
            x = dad[top[x]];
        }
        return deep[x] > deep[y] ? y : x;
    }
    int main() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) fa[i] = i;
        cnt = n;
        for(int i = 1; i <= m; i++) 
            scanf("%d%d%d", &e[i].from, &e[i].to, &e[i].w);
        sort(e + 1, e + 1 + m, cmp);
        for(int i = 1; i <= m; i++) {
            int fx = find(e[i].from), fy = find(e[i].to);
            if(fx != fy) {
                va[++cnt] = e[i].w;
                fa[fx] = fa[fy] = fa[cnt] = cnt;
                add(fx, cnt);add(cnt, fx);
                add(fy, cnt);add(cnt, fy);    
            }
        }
        for(int i = 1; i <= cnt; i++)
            if(!vis[i]) {
                int f = find(i);
                dfs(f, 0), dfs1(f, f);
            }
        /*for(int i = 1; i <= cnt; i++)
            printf("%d ", size[i]);*/
        scanf("%d", &q);
        while(q--) {
            scanf("%d%d", &n, &m);
            if(find(n) != find(m)) printf("-1
    ");
            else printf("%d
    ", va[lca(n, m)]);
        }
        return 0;
    }

    谢谢收看, 祝身体健康!

  • 相关阅读:
    android gallery 自定义边框+幻灯片
    C/C++学习笔记---高地址、低地址、大段字节序、小段字节序
    C#学习笔记--详解委托,事件与回调函数
    Clr Via C#读书笔记---计算限制的异步操作
    Clr Via C#读书笔记---CLR寄宿和应用程序域
    Clr Via C#读书笔记---程序集的加载和反射
    Clr Via C#读书笔记---垃圾回收机制
    Clr Via C#读书笔记---线程基础
    CLR via C#(18)——Enum
    CLR via C#(17)--接口
  • 原文地址:https://www.cnblogs.com/yanxiujie/p/11438055.html
Copyright © 2011-2022 走看看