• 题解报告:hdu 1233 还是畅通工程


    Problem Description
    某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
    Input
    测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
    当N为0时,输入结束,该用例不被处理。
    Output
    对每个测试用例,在1行里输出最小的公路总长度。
    Sample Input
    3
    1 2 1
    1 3 2
    2 3 4
    4
    1 2 1
    1 3 4
    1 4 1
    2 3 3
    2 4 2
    3 4 5
    0
    Sample Output
    3
    5
    Huge input, scanf is recommended.
    解题思路:①Prim算法的核心就是加点法,每次把离目标集合最小权值的一点加入其中。其算法跟Dijkstra大同小异,不同的是mincost数组记录的是当前点到目标集合的最小权值,下次就取最小权值的端点加入就可以了。其算法时间复杂度是O(n2),适合于稠密图。
    ②Kruskal算法的核心就是加边法,并且运用到并查集。先将各边权值按升序排列,然后贪心加边,加边时查看当前边的两端点是否为同一个连通图中的两点,如果是的话,就不能纳入目标集合,因为会出现回路,即产生圈,这样就不满足树的定义了,否则将其加入。其原来的时间复杂度是O(n2),使用并查集之后时间复杂度是O(elogn),适合于稀疏图。
    AC代码之Prim算法:
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int INF = 0x3f3f3f3f;
     4 const int maxn = 105;
     5 int n,a,b,c,mincost[maxn],cost[maxn][maxn];
     6 bool vis[maxn];
     7 int Prim(){//加点法
     8     for(int i=1;i<=n;++i)//这里选取节点1作为起点,mincost为各节点到最小生成树节点集合的最小权值
     9         mincost[i]=cost[1][i];
    10     mincost[1]=0;vis[1]=true;//标记已访问
    11     int res=0;//计算最小生成树的权值
    12     for(int i=1;i<n;++i){
    13         int k=-1;//标记为-1
    14         for(int j=1;j<=n;++j)//找出到最小生成树节点集合的权值最小的还没入集合的一点
    15             if(!vis[j] && (k==-1 || mincost[k]>mincost[j]))k=j;
    16         if(k==-1)break;//如果还是-1,表示已经完成最小生成树的建立
    17         vis[k]=true;//将节点k纳入最小生成树节点的集合
    18         res+=mincost[k];//加上其权值
    19         for(int j=1;j<=n;++j)//更新k的邻接点到最小生成树节点集合的最小权值
    20             if(!vis[j])mincost[j]=min(mincost[j],cost[k][j]);//还没归纳的节点
    21     }
    22     return res;
    23 }
    24 int main()
    25 {
    26     while(~scanf("%d",&n) && n){
    27         memset(vis,false,sizeof(vis));
    28         for(int i=1;i<=n;++i){
    29             for(int j=1;j<=n;++j)
    30                 cost[i][j]=(i==j?0:INF);
    31         }
    32         for(int i=1;i<=n*(n-1)/2;++i){
    33             cin>>a>>b>>c;
    34             cost[a][b]=cost[b][a]=c;
    35         }
    36         printf("%d
    ",Prim());
    37     }
    38     return 0;
    39 }

    AC代码之Kruskal算法:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,father[105],height[105];
     4 struct edge{int u,v,cost;}es[5000];
     5 bool cmp(const edge& e1,const edge& e2){
     6     return e1.cost<e2.cost;
     7 }
     8 void init_union_find(){//将每个节点当作根节点
     9     for(int i=1;i<=n;++i)father[i]=i;
    10 }
    11 int find_father(int x){//递归查找根节点
    12     if(father[x]==x)return x;
    13     else return father[x]=find_father(father[x]);
    14 }
    15 bool same_father(int x,int y){//查找两点的根节点是否相同
    16     return find_father(x)==find_father(y);
    17 }
    18 void unite(int x,int y){
    19     x=find_father(x);
    20     y=find_father(y);
    21     if(x==y)return;//如果为同一个连通图,直接返回
    22     if(height[x]<height[y])father[x]=y;//高度大的当作高度小的父亲
    23     else{
    24         father[y]=x;
    25         if(height[x]==height[y])height[x]++;//如果合并前两棵树的高度相等,加入一节点后树的高度就加1
    26     }
    27 }
    28 int Kruskal(){
    29     sort(es+1,es+n*(n-1)/2+1,cmp);//权值按从小到大排序
    30     init_union_find();//并查集的初始化
    31     int res=0;
    32     for(int i=1;i<=n*(n-1)/2;++i){
    33         edge e=es[i];
    34         if(!same_father(e.u,e.v)){//如果两点的根节点不同,即为非连通图
    35             unite(e.u,e.v);//归并到同一个集合
    36             res+=e.cost;//加入权值
    37         }
    38     }
    39     return res;
    40 }
    41 int main()
    42 {
    43     while(~scanf("%d",&n) && n){
    44         for(int i=1;i<=n*(n-1)/2;++i)
    45             scanf("%d %d %d",&es[i].u,&es[i].v,&es[i].cost);
    46         memset(height,0,sizeof(height));//初始化树的高度为0
    47         printf("%d
    ",Kruskal());
    48     }
    49     return 0;
    50 }

    克鲁斯卡尔简化版代码:

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 int n,father[105],sum;
     4 struct edge{int u,v,cost;}es[5000];
     5 bool cmp(const edge& e1,const edge& e2){
     6     return e1.cost<e2.cost;
     7 }
     8 void init_union_find(){//将每个节点当作根节点
     9     for(int i=1;i<=n;++i)father[i]=i;
    10 }
    11 int find_father(int x){//递归查找根节点
    12     if(father[x]==x)return x;
    13     else return father[x]=find_father(father[x]);
    14 }
    15 void unite(int x,int y,int z){
    16     x=find_father(x);
    17     y=find_father(y);
    18     if(x!=y){//如果边的两端点的根节点不相同,即分别为非连通图,则可以归并
    19         sum+=z;//加上最小权值
    20         father[x]=y;//将x的父节点改成y,也就是现在x的根节点是y的根节点,注意这里只是简单的修改,和上面归纳到同一个集合不太一样
    21     }
    22 }
    23 int main()
    24 {
    25     while(~scanf("%d",&n) && n){
    26         for(int i=1;i<=n*(n-1)/2;++i)
    27             scanf("%d %d %d",&es[i].u,&es[i].v,&es[i].cost);
    28         sort(es+1,es+n*(n-1)/2+1,cmp);//权值按从小到大排序
    29         sum=0;init_union_find();//初始化
    30         for(int i=1;i<=n*(n-1)/2;++i)
    31             unite(es[i].u,es[i].v,es[i].cost);
    32         printf("%d
    ",sum);
    33     }
    34     return 0;
    35 }
  • 相关阅读:
    可能是最简单的解决本地开发接口请求跨域问题的方案
    《Visual Studio程序员箴言》笔记
    vue后台项目记录
    新版本微信导致的ios表单bug
    longzhuapp项目笔记
    Session、LocalStorage、SessionStorage、Cache-Ctrol比较
    css3实现不同进度条
    axios请求接口的踩坑之路
    实现不同尺寸的图片在固定的区块内实现水平垂直居中
    for循环查找元素怎么跳出for循环
  • 原文地址:https://www.cnblogs.com/acgoto/p/9053764.html
Copyright © 2020-2023  润新知