• 最小生成树 & 洛谷P3366【模板】最小生成树 & 洛谷P2820 局域网


    嗯...

     

    理解生成树的概念:

    在一幅图中将所有n个点连接起来的n-1条边所形成的树。

    最小生成树:

    边权之和最小的生成树。

    最小瓶颈生成树:

    对于带权图,最大权值最小的生成树。

    如何操作?

    1.Prim算法(O(mlogn)

    2.Kruskal算法(O(mlogn)

     推荐使用第二种,无需建图。

    算法流程:

    Prim算法:(思想类似dijkstra

      随意选取一个点作为已访问集合的第一个点,并将所有相连的边加入堆中
      从堆中找到最小的连接集合内和集合外点的边,将边加入最小生成树中
      将集合外点标记为已访问,并将相连边加入堆
      重复以上过程直到所有点都在访问集合中

    Kruskal算法:(并查集思想)

      将边按照权值排序
      依次枚举每一条边,若连接的两点不连通则加入最小生成树中
      使用并查集维护连通性

    模板代码:

     1 int f[101], h;
     2 struct node{
     3     int x, y, l;
     4 } a[100001];
     5 inline bool cmp(node i, node j){
     6     return i.l < j.l;
     7 }
     8 inline int find(int x){
     9     if(x != f[x])//本身是否为父亲节点
    10         f[x] = find(f[x]);
    11     return f[x];
    12 }//并查集操作
    13 int main(){
    14     for(int i = 1; i <= n; i++){
    15         f[i] = i;
    16     }//父节点初始化
    17     sort(a+1, a+k+1, cmp);//排序
    18     for(int i = 1; i <= k; i++){
    19         int r1 = find(a[i].x);
    20         int r2 = find(a[i].y);
    21         if(r1 != r2){
    22             f[r1] = r2;
    23         }
    24     }
    25 }
    Kruskal
    #include<bits/stdc++.h>
    using namespace std;
    int n,m,a,b,c;
    int sum;
    int g[1001][1001],minn[1001];
    bool u[1001]; 
    int main(){
        memset(g,0x7f,sizeof(g));
        memset(minn,0x7f,sizeof(minn));
        memset(u,true,sizeof(u));
        cin>>n>>m;
        for(int i=1;i<=m;i++)
        {
            cin>>a>>b>>c;
            g[a][b]=g[b][a]=c;
            sum+=c;
        }
        minn[1]=0;
        for(int i=1;i<=n;i++){
            int k=0;
            for(int j=1;j<=n;j++)
            if(u[j]&&minn[j]<minn[k])
            k=j;  
            u[k]=false;
            for(int j=1;j<=n;j++)
            if(u[j]&&g[k][j]<minn[j])
            minn[j]=g[k][j];
        }
        int total=0;
        for(int i=1;i<=n;i++) 
        total+=minn[i];
        cout<<sum-total<<endl;
        return 0;
    }
    Prim

    模板题:

    洛谷P3366【模板】最小生成树:

    题目链接:https://www.luogu.org/problemnew/show/P3366

     

    思路:(Kruskal)

    一道模板题,首先用一个结构体读入,然后初始化父节点,再按边权排序,然后用find函数分别找输入时的两个点的父节点,并判断其中一个是否是另一个的父亲,否则就进行合并,并将h+=a[i].l。(思路比较好理解)

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 
     5 using namespace std;
     6 
     7 int h, f[200005];
     8 
     9 struct node{
    10     int x, y, l;
    11 } a[200005];
    12 
    13 inline bool cmp(node i, node j){
    14     return i.l < j.l;
    15 }
    16 
    17 inline int find(int x){
    18     if(x != f[x])
    19         f[x] = find(f[x]);
    20     return f[x];
    21 }
    22 
    23 int main(){
    24     int n, m;
    25     scanf("%d%d", &n, &m);
    26     for(int i = 1; i <= n; i++){
    27         f[i] = i;
    28     }
    29     for(int i = 1; i <= m; i++){
    30         scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].l);
    31         //h += a[i].l;
    32     }
    33     sort(a+1, a+m+1, cmp);
    34     for(int i = 1; i <= m; i++){
    35         int r1 = find(a[i].x);
    36         int r2 = find(a[i].y);
    37         if(r1 != r2){
    38             f[r1] = r2;
    39             h += a[i].l;
    40             //h -= a[i].l;
    41         }
    42     }
    43     printf("%d", h);
    44     return 0;
    45 }
    AC代码

    洛谷P2820 局域网:

    题目链接:https://www.luogu.org/problemnew/show/P2820

    思路:

    首先这道题的问法就很模板:

    很显然“f(i,j)表示i,j之间连接的畅通程度”即为i到j点的权值;“除去一些连线,使得网络中没有回路,并且被除去网线的Σf(i,j)最大”很显然是求最小生成树。但注意一个细节,它与最小生成树有所不同,它要求的是Σf(i,j)最大

    所以我们在最小生成树的模板上进行修改即可:读入时将所有的边权都加到h中。在判断父节点是否相同时,若不同,则将合并,并将合并的这条边的权值减掉即可。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 
     5 using namespace std;
     6 
     7 int f[101], h;
     8 
     9 struct node{
    10     int x, y, l;
    11 } a[100001];
    12 
    13 inline bool cmp(node i, node j){
    14     return i.l < j.l;
    15 }
    16 
    17 inline int find(int x){
    18     if(x != f[x])
    19         f[x] = find(f[x]);
    20     return f[x];
    21 }
    22 
    23 int main(){
    24     int n, k;
    25     scanf("%d%d", &n, &k);
    26     for(int i = 1; i <= n; i++){
    27         f[i] = i;
    28     }
    29     for(int i = 1; i <= k; i++){
    30         scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].l);
    31         h += a[i].l;
    32     }
    33     sort(a+1, a+k+1, cmp);
    34     for(int i = 1; i <= k; i++){
    35         int r1 = find(a[i].x);
    36         int r2 = find(a[i].y);
    37         if(r1 != r2){
    38             f[r1] = r2;
    39             h -= a[i].l;
    40         }
    41     }
    42     printf("%d", h);
    43     return 0;
    44 }
    AC代码

    大概就是这样,个人认为Kruskal算法比Prim算法写起来简单并好理解....

  • 相关阅读:
    C++ 内置宏定义 与 预编译指令
    C++ 反射机制的简单实现
    Intellij打包jar文件,“java.lang.SecurityException: Invalid signature file digest for Manifest main attrib
    IntelliJ IDEA 运行你的第一个Java应用程序 idea运行main方法
    IDEA重新打jar包时报错MANIFEST.MF already exists in VFS
    在 RPA10.X 运行异常,RPA9 却正常的问题处理
    Python判断文件是否存在的三种方法
    Python日期时间函数
    Chrome提示:"请停用以开发者模式运行的扩展程序"的解决办法
    Win10提示“无法打开此计算机上的组策略对象”如何解决
  • 原文地址:https://www.cnblogs.com/New-ljx/p/10779353.html
Copyright © 2020-2023  润新知