还是畅通工程--最小生成树+并查集

某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。 
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
分析:
学过树的人应该知道,这道题目其实就是求解最小生成树,最小生成树有两种解法普里姆算法和克鲁斯卡尔算法。这里采用的是第二种算法,其实也就是采用贪心的思想。我简单描述一下这个算法思想:
我们把一开始的顶点集合视为n个孤立的集合,然后将边按照权值大小进行排序,先选取一个权值最小的边,并将该边的两个点加入已经满足的集合,重复此步骤,如果加入的边使图成为了环那么就要舍去,如果将所有的点都加入进来了,那么此时的图就是最小生成树了。
所以我们利用并查集判断是否成环,判断的条件就是如果输入的边的两个顶点的父节点相同,那么就要舍去这条边。因为如果父节点相同就证明通过之前的合并已经在我们的集合里面了,再加进去就会形成环。
上代码:
 
  
import java.util.*;
public class Main {
   static Scanner in = new Scanner(System.in);
   static int[] f = new int[105];
   static void init(){
	   for (int i = 1; i <= 100; i++) {
		    f[i]=i;
	   }
   }
   static int find(int x){
	   int t= x;
	   while(t!=f[t]){
		   t=f[t];
	   }
	   return t;
   }
   static void merge(int x,int y){
	   int t1 = find(x);
	   int t2 = find(y);
	   if(t1!=t2)
		   f[t1]=t2;
   }
   static Comparator com = new Comparator() {	
	  @Override
	public int compare(Point o1, Point o2) {
		return o1.s-o2.s;
	}
};
	public static void main(String[] args) {
		List st = new ArrayList();
		int sum = 0;
		while(in.hasNext()){
			sum=0;
			init();
			st.clear();
			int n = in.nextInt();
			 if(n==0) break;
			 int x,y,s;
			for (int i = 0; i < (n-1)*n/2; i++) {
				x=in.nextInt();
				y=in.nextInt();
				s=in.nextInt();
				Point p = new Point(x,y,s);
				st.add(p);
			 }
			Collections.sort(st,com);
			for (int i = 0; i < (n-1)*n/2; i++) {
				 if(find(st.get(i).x)!=find(st.get(i).y)){//如果父节点相同,那么就会形成环,舍去,反之不是环
				   merge(st.get(i).x,st.get(i).y);
					 sum+=st.get(i).s;
				}
			 }
			System.out.println(sum);
		}
	}
  }

class Point{
	int x,y,s;
	Point(int x,int y,int s){
		this.x=x;
		this.y=y;
		this.s=s;
	};
}


你可能感兴趣的:(树论,并查集)