克鲁斯卡尔+并查集(最小生成树)详解--继续畅通工程

Description

省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可)。现得到城镇道路统计表,表中列出了任意两城镇间修建道路的费用,以及该道路是否已经修通的状态。现请你编写程序,计算出全省畅通需要的最低成本。 

 

Input

测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( 1< N < 100 );随后的 N(N-1)/2 行对应村庄间道路的成本及修建状态,每行给4个正整数,分别是两个村庄的编号(从1编号到N),此两村庄间道路的成本,以及修建状态:1表示已建,0表示未建。 

当N为0时输入结束。

 

Output

每个测试用例的输出占一行,输出全省畅通需要的最低成本。

 

Sample Input

 

3

1 2 1 0

1 3 2 0

2 3 4 0

3

1 2 1 0

1 3 2 0

2 3 4 1

3
1 2 1 0

1 3 2 1

2 3 4 1

0

 

Sample Output

3

1

0

克鲁斯卡尔算法也是求最小生成树的一种方法,与prim算法不同的是,克鲁斯卡尔使用边为对象进行操作,prim使用点操作;克鲁斯卡尔利用结构题存储图中每条边的信息,包括:两端的点,边的权值,以及根据题目添加的其他信息.利用排序先把边从小到大排序.在这里一个非常重要的点,运用了并查集,那么并查集究竟用来干嘛呢?(并查集是用来进行判定两个点之间是不是可以连通的,假设现在有两个点A1和A2,他俩能不能互相到达呢,这时候我们用并查集去找他们能够到达的其他的点;假设A1找到了H,A2也找到了H点,那么他俩是可以相互到达的,也就是连同的;反之,A1找到了D,A2找到了H,不相同,那么他俩是不能够连通的.)(并查集完成步骤)

回归正题,首先,并查集将每个点的父亲节点设置为自己本身,然后遍历每条边的两个点,将这两个点连接,依次进行.因为你已经是将所有边按照从小到大顺序排列,所以当你成功查找n-1条边也就完成了最小生成树的建立。而设置的变量则将最小生成树的权值和输出。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#define MAX 100007
using namespace std;
struct R
{
    int u;
    int v;
    int w;
    int b;
} rute[MAX];//定义结构体,包括边的两个端点,边的权值,边是否已经存在(用b来表示)
bool cmp(R a,R b)
{
    return a.w<b.w;//按照从小到大的顺序排列
}
int n,m,f[MAX],sum,c;
void init()
{
    for(int i=1; i<=n; i++)
    {
        f[i]=i;//初始化,将每个点的父亲节点设置为其本身
    }
}
int father(int x)
{
    int r;
    r=x;
    while(f[r]!=r)
        r=f[r];
    return r;
}
int Find(int v,int u)//将两个点连接
{
    int t1=father(v);
    int t2=father(u);
    if(t1!=t2)
    {
        f[t2]=t1;
        return 1;
    }
    return 0;
}
int main()
{
    int i;
    while(~scanf("%d",&n))
    {
        if(n==0)
            break;
        if(n==1)
        {
            printf("0\n");
            continue;
        }
        int m,x;
        m=n*(n-1)/2;
        for(i=1; i<=m; i++)
        {
            scanf("%d %d %d %d",&rute[i].u,&rute[i].v,&rute[i].w,&rute[i].b);
            if(rute[i].b)
                rute[i].w=0;
        }
        sort(rute+1,rute+m+1,cmp);
        init();
        c=sum=0;
        for(i=1; i<=m; i++)
        {
            if(Find(rute[i].u,rute[i].v))
            {
                c+=1;
                sum+=rute[i].w;
            }
            if(c==n-1)
                break;
        }
        printf("%d\n",sum);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值