某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
当N为0时,输入结束,该用例不被处理。
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
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<Point> com = new Comparator<Point>() { @Override public int compare(Point o1, Point o2) { return o1.s-o2.s; } }; public static void main(String[] args) { List<Point> st = new ArrayList<Point>(); 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; }; }