zoukankan      html  css  js  c++  java
  • hdu1233+最小生成树

    某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。

    测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。
    当N为0时,输入结束,该用例不被处理。

    对每个测试用例,在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

    最简单的生成树题,拿来总结算法的。

    Kruscal算法:(用并查集)

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 struct node
     6 {
     7     int x,y,s;
     8 }edge[11000];//存边的两端点和边长
     9 int sset[110];
    10 bool cmp(node x1,node y1)
    11 {
    12     return x1.s<y1.s;
    13 }
    14 int ffind(int x)//并查集中的"查"
    15 {
    16     int r=x;
    17     while(r!=sset[r])
    18         r=sset[r];
    19     return r;
    20 }
    21 void mmerge(int a,int b)//并查集中的"并"
    22 {
    23     int aa=ffind(a);
    24     int bb=ffind(b);
    25     if(aa!=bb)
    26         sset[aa]=bb;
    27 }
    28 int kruscal(int nn,int mm)//nn是点的个数,mm是边数
    29 {
    30     int ii,num=0,sum=0;//num记录已加入生成树的边的条数,sum记录生成树的权值
    31     for(ii=0;ii<mm;ii++)
    32     {
    33         int from=ffind(edge[ii].x);
    34         int to=ffind(edge[ii].y);
    35         if(from!=to)//如果两端点不属于同一个集合,并入生成树
    36         {
    37             mmerge(from,to);
    38             sum+=edge[ii].s;//更新权值
    39             num++;
    40         }
    41         if(num==nn-1) break;//生成树最多nn-1条边
    42     }
    43     if(num<nn-1) return -1;//说明最小生成树不存在
    44     else return sum;
    45 }
    46 int main()
    47 {
    48     int n,i;
    49     while(~scanf("%d",&n)&&n)
    50     {
    51         int m=(n-1)*n/2;
    52         for(i=1;i<=n;i++)
    53             sset[i]=i;
    54         for(i=0;i<m;i++)
    55         scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].s);
    56         sort(edge,edge+m,cmp);//一定要记得将边按长度从小到大排序!!!!
    57         printf("%d\n",kruscal(n,m));
    58     }
    59     return 0;
    60 }

    优先队列优化的prim算法:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<queue>
     4 #include<vector>
     5 using namespace std;
     6 const int mmax=1<<30;
     7 struct node
     8 {
     9     int v,s;//v存边的一个端点,另一个端点已知,s存边权值
    10     node(int vv,int ss):v(vv),s(ss){}//一个构造函数
    11     bool operator <(const node &e)const//优先弹出边权值小的边
    12     {
    13         return s>e.s;
    14     }
    15 };
    16 vector<vector<node> > g(110);//有110个元素的一个动态数组,数组里的每个元素又是一个放结构体的动态数组
    17 int prim(const vector<vector<node> > &g,int nn)
    18 {
    19     int i;
    20     node x(1,0);//x不赋初值会报错,初值表示端点1,边长0
    21     priority_queue<node> q;
    22     int vis[110],dis[110];//vis标记每个点是否已经加入到树中了,dis记录每个点到当前生成树的距离
    23     int num=0;//记录生成树中已有边数
    24     int sum=0;//记录生成树的权值
    25     for(i=1;i<=nn;i++)
    26     {
    27         vis[i]=0;
    28         dis[i]=mmax;
    29     }//初始化,清除标记,距离赋为无穷大
    30     q.push(node(1,0));//把第一条边放进去,实际是一个点,边长为0
    31     while(num<nn&&!q.empty())
    32     {
    33         do{
    34             x=q.top();
    35             q.pop();
    36         }while(vis[x.v]!=0&&!q.empty());//先无条件执行一次循环体,找到一个vis[x.v]=0的边(即边的一个端点没有被标记)跳出循环
    37         sum+=x.s;//更新生成树权值
    38         vis[x.v]=1;//更新标记
    39         num++;//更新边数
    40         for(i=0;i<g[x.v].size();i++)//对于与这个端点相邻的每条边
    41         {
    42             int k=g[x.v][i].v;//k记录相邻边的端点
    43             if(vis[k]==0)
    44             {
    45                 int ss=g[x.v][i].s;//ss记录相邻边的权值
    46                 if(ss<dis[k])//更新相邻边的端点到生成树的距离
    47                 {
    48                     dis[k]=ss;
    49                     q.push(node(k,ss));//把这条边加入优先队列
    50                 }
    51             }
    52         }
    53     }
    54     if(num<nn-1) return -1;
    55     else return sum;
    56 }
    57 int main()
    58 {
    59     int n,i;
    60     while(~scanf("%d",&n)&&n)
    61     {
    62         for(i=1;i<=n;i++)
    63         g[i].clear();//一定要先清空容器!!
    64         int m=n*(n-1)/2;
    65         int a,b,c;
    66         for(i=0;i<m;i++)
    67         {
    68             scanf("%d%d%d",&a,&b,&c);
    69             g[a].push_back(node(b,c));//a:边的一个端点,b:边的另一个端点,c:边权值
    70             g[b].push_back(node(a,c));//无向图,再反向加入一遍
    71         }
    72         printf("%d\n",prim(g,n));
    73     }
    74     return 0;
    75 }

    未优化的prim算法:

     1 #include<cstdio>
     2 #include<cstring>
     3 using namespace std;
     4 int g[110][110];
     5 const int mmax=1<<30;
     6 int prim(int n)
     7 {
     8     int lowcost[110],pre[110],vis[110],i;//lowcost记录两端点分属U(在树中),V(不在树中)两集合的边的权值,i记录其在V中的端点,pre[i]记录其在U中的端点
     9     memset(vis,0,sizeof(vis));
    10     vis[1]=1;//起点1加入U
    11     for(i=1;i<=n;i++)//初始化所有点在U中的附着点为起点1,初始化lowcost值为起点1到所有点的距离
    12     {
    13         pre[i]=1;
    14         lowcost[i]=g[1][i];
    15     }
    16     int num=0;
    17     int sum=0;
    18     while(num<n-1)
    19     {
    20         int mmin=mmax,vx;
    21         for(i=1;i<=n;i++)//选出lowcost中的最小边权值
    22         {
    23             if(!vis[i]&&lowcost[i]<mmin)
    24             {
    25                 mmin=lowcost[i];
    26                 vx=i;
    27             }
    28         }
    29         vis[vx]=1;//新点vx加入
    30         num++;
    31         sum+=g[vx][pre[vx]];
    32         for(i=1;i<=n;i++)//加入边后要更新lowcost
    33         {
    34             if(!vis[i]&&g[vx][i]<lowcost[i])
    35             {
    36                 lowcost[i]=g[vx][i];
    37                 pre[i]=vx;
    38             }
    39         }
    40     }
    41     return sum;
    42 }
    43 int main()
    44 {
    45     int n,i,j;
    46     while(scanf("%d",&n)!=EOF&&n!=0)
    47     {
    48         int m=(n-1)*n/2;
    49         for(i=1;i<=n;i++)
    50         {
    51             for(j=1;j<=n;j++)
    52             {
    53                 if(i==j) g[i][j]=0;
    54                 else g[i][j]=mmax;
    55             }
    56         }
    57         for(i=0;i<m;i++)
    58         {
    59             int a,b,c;
    60             scanf("%d%d%d",&a,&b,&c);
    61             g[a][b]=g[b][a]=c;
    62         }
    63         printf("%d\n",prim(n));
    64     }
    65     return 0;
    66 }
  • 相关阅读:
    cf B. Sereja and Suffixes
    cf E. Dima and Magic Guitar
    cf D. Dima and Trap Graph
    cf C. Dima and Salad
    最短路径问题(floyd)
    Drainage Ditches(网络流(EK算法))
    图结构练习—BFSDFS—判断可达性(BFS)
    Sorting It All Out(拓扑排序)
    Power Network(最大流(EK算法))
    Labeling Balls(拓扑)
  • 原文地址:https://www.cnblogs.com/mt522/p/5249116.html
Copyright © 2011-2022 走看看