HDU 1233.还是畅通工程【最小生成树 kruskal算法(并查集)+prim算法】【1月8】

还是畅通工程

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
很明显就是最小生成树。说一下kruskal算法:

1).记Graph中有v个顶点,e个边
2).新建图G,G中拥有原图中相同的e个顶点,但没有边
3).将原图Graph中所有e个边按权值从小到大排序
4).循环:从权值最小的边开始遍历每条边,如果边连接的两顶点不在同一连通分量中,则加入这条边;否则不加。直至图Graph中所有的节点都在同一个连通分量中

当然还有一种prim算法:

1)一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
    a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
    b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;

就这道题而言,prim算法比kruskal算法稍快一点

当然算法的选取还是看题啦:稠密图用prim算法,稀疏图用kruskal算法。

kruskal代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int N, ans, flag[110];
//最小生成树kruskal算法
struct ss
{
    int a, b, len;
} f[5100];
bool cmp(ss x, ss y)
{
    if(x.len < y.len) return true;
    else return false;
}
int fi(int z)
{
    if(flag[z] == z) return z;
    else return flag[z] = fi(flag[z]);//路径压缩
}
int main()
{
    while(scanf("%d", &N) && N)
    {
        ans = 0;
        int m = N*(N-1)/2;
        for(int i = 0;i <= N; ++i) flag[i] = i;
        for(int i = 0;i < m; ++i) scanf("%d %d %d", &f[i].a, &f[i].b, &f[i].len);
        sort(f, f+m, cmp);
        for(int i = 0;i < m; ++i)
        {
            int x = fi(f[i].a);
            int y = fi(f[i].b);
            if(x != y)//不连通就将其连通起来
            {
                flag[x] = y;
                ans += f[i].len;//求和
            }
        }
        cout << ans << endl;
    }
    return 0;
}

prim算法如下:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAXN 99999999;
const int maxn = 110;
int Map[maxn][maxn], l[maxn], flag[maxn];
int N, M, fr, to, pos, len, ans, minlen;
int main()
{
    while(scanf("%d", &N) && N)
    {
        //初始化
        ans = 0;
        minlen = MAXN;
        memset(flag, 0, sizeof(flag));
        memset(Map, 0, sizeof(Map));
        M = N*(N-1)/2;
        for(int i = 0;i <= N; ++i) l[i] = MAXN;

        //输入
        for(int i = 0;i < M; ++i)
        {
            scanf("%d %d %d", &fr, &to, &len);
            Map[fr][to] = len;
            Map[to][fr] = len;
        }

        //prim算法
        flag[1] = 1;//默认从节点1开始
        for(int i = 1;i <= N; ++i)  l[i] = Map[1][i];//初始化
        for(int i = 1;i < N; ++i)
        {
            minlen = 1 << 30;
            for(int j = 1;j <= N; ++j)//找出最近到达的下一节点
            {
                if(!flag[j] && l[j] < minlen)
                {
                    minlen = l[j];
                    pos = j;//记录下一节点
                }
            }
            flag[pos] = 1;//标记
            ans += l[pos];//求和
            for(int j = 1;j <=N; ++j)//更新权值
            {
                if(!flag[j] && l[j] > Map[pos][j] && Map[pos][j])
                {
                    l[j] = Map[pos][j];
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}



你可能感兴趣的:(C++,最小生成树,ACM,HDU,kruskal)