户户通电——最小生成树的Prim和Kruscal算法java实现

问题描述

2015年,全中国实现了户户通电。作为一名电力建设者,小明正在帮助一带一路上的国家通电。
  这一次,小明要帮助 n 个村庄通电,其中 1 号村庄正好可以建立一个发电站,所发的电足够所有村庄使用。
  现在,这 n 个村庄之间都没有电线相连,小明主要要做的是架设电线连接这些村庄,使得所有村庄都直接或间接的与发电站相通。
  小明测量了所有村庄的位置(坐标)和高度,如果要连接两个村庄,小明需要花费两个村庄之间的坐标距离加上高度差的平方,形式化描述为坐标为 (x_1, y_1) 高度为 h_1 的村庄与坐标为 (x_2, y_2) 高度为 h_2 的村庄之间连接的费用为
  sqrt((x_1-x_2)(x_1-x_2)+(y_1-y_2)(y_1-y_2))+(h_1-h_2)*(h_1-h_2)。
  在上式中 sqrt 表示取括号内的平方根。请注意括号的位置,高度的计算方式与横纵坐标的计算方式不同。
  由于经费有限,请帮助小明计算他至少要花费多少费用才能使这 n 个村庄都通电。

输入格式

输入的第一行包含一个整数 n ,表示村庄的数量。
  接下来 n 行,每个三个整数 x, y, h,分别表示一个村庄的横、纵坐标和高度,其中第一个村庄可以建立发电站。

输出格式

输出一行,包含一个实数,四舍五入保留 2 位小数,表示答案。

样例输入

4
1 1 3
9 9 7
8 8 6
4 5 4

样例输出

17.41

评测用例规模与约定

对于 30% 的评测用例,1 <= n <= 10;
  对于 60% 的评测用例,1 <= n <= 100;
  对于所有评测用例,1 <= n <= 1000,0 <= x, y, h <= 10000。

分析
无向网的最小生成树问题
prim算法,选点,适合边多的网,时间复杂度O(n^2) 不会超时

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Scanner;

class Main
{
	public static void main(String[] args)
	{
		Scanner sc =new Scanner(System.in);
		int n=sc.nextInt();
		Ver[] graph= new Ver[n];
		for (int i = 0; i < n; i++)
		{
			graph[i]=new Ver(sc.nextDouble(), sc.nextDouble(), sc.nextDouble());
		}
		double dis[] =new double[n];
		//初始化
		for (int i = 1; i < n; i++)
		{
			dis[i]=graph[0].getDis(graph[i]);
		}
		double ans =0;
		for (int i = 1; i < n; i++)
		{
			int cur =getMin(dis);
			ans+=dis[cur];
			dis[cur]=0;
			for (int j = 0; j < n; j++)
			{
				if(dis[j]!=0&&graph[cur].getDis(graph[j])<dis[j])
					dis[j]=graph[cur].getDis(graph[j]);
			}
		}
		System.out.printf("%.2f",ans);
		
	}
	static int getMin(double[] a)
	{
		int tar =0;
		double min= Double.MAX_VALUE;
		for (int i = 0; i < a.length; i++)
		{
			if(min>a[i]&&a[i]!=0)
			{
				min=a[i];
				tar=i;
			}
		}
		return tar;
	}
	static class Ver//定义节点
	{
		double x,y,h;
		public Ver(double x,double y,double h)
		{
			// TODO Auto-generated constructor stub
			this.x=x;
			this.y=y;
			this.h=h;
		}
		public double getDis(Ver a)
		{
			return  Math.sqrt((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y))+(a.h-h)*(a.h-h);
		}
	}
}

Kruscal算法,选边,适合边少的,时间复杂度O(eloge)主要在排序上

import java.util.Arrays;
import java.util.Scanner;

class Main
{
	public static void main(String[] args)
	{
		Scanner sc =new Scanner(System.in);
		int n=sc.nextInt();
		Ver[] graph= new Ver[n];
		int[] father = new int[n];//找祖先
		for (int i = 0; i < n; i++)
		{
			graph[i]=new Ver(sc.nextDouble(), sc.nextDouble(), sc.nextDouble());
			father[i]=i;
		}
		Road[] roads =new Road[(n-1)*n/2];
		//初始化
		for (int i = 0,cur=0; i < n-1; i++)
		{
			for (int j = i+1; j < n; j++)
			{
				roads[cur]=new Road(i, j,graph);
				cur++;
			}
		}
		double ans =0;
		Arrays.sort(roads);
		for (int i = 0; i < roads.length; i++)
		{
			System.out.println(roads[i].dis);
		}
		//kruscal算法
		for (int i = 0,num=0; i < roads.length; i++,num++)
		{
			if(num==n-1) break;
			int a=fatch(roads[i].from,father);
			int b=fatch(roads[i].to,father);
			if(a!=b)
			{
				ans+=roads[i].dis;
				father[a]=b;
			}
		}
		System.out.printf("%.2f",ans);
		
	}
	
	private static int fatch(int a, int[] father)
	{
		// TODO Auto-generated method stub
		while(father[a]!=a)
		{
			a=father[a];
		}
		return a;
	}

	

	static class Road implements Comparable<Road>//定义边
	{
		int from;
		int to;
		double dis;

		
		public Road(int i, int j, Ver[] graph)
		{
			// TODO Auto-generated constructor stub
			this.from = i;
			this.to = j;
			this.dis=graph[from].getDis(graph[to]);
		}

		public int compareTo(Road x)
		{
			double d= (this.dis-x.dis);
			if(d>0) return 1;
			if(d==0) return 0;
			return -1;

		}
				
	}
	static class Ver//定义节点
	{
		double x,y,h;
		public Ver(double x,double y,double h)
		{
			// TODO Auto-generated constructor stub
			this.x=x;
			this.y=y;
			this.h=h;
		}
		public double getDis(Ver a)
		{
			return  Math.sqrt((a.x-x)*(a.x-x)+(a.y-y)*(a.y-y))+(a.h-h)*(a.h-h);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值