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;
}