问题描述:在一个按照东西和南北方向划分成规整街区的城市里,n个居民点散乱地分布在不同的街区中。用x坐标表示东西向,用y坐标表示南北向。各居民点的位置可以由坐标(x,y)表示。
要求:为建邮局选址,使得n个居民点到邮局之距离的总和最小。
提示:带权中位数(分治算法)
package postaddress;
import java.util.Scanner;
import java.io.*;
public class PostAddress {
public static void main(String[] args) throws Exception {
while(true){
//选择要输入的测试数据文件的编号
System.out.println("请输入数据文件编号(1~5):");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
if(num<=0 || num>=6){
System.out.println("编号错误请重新输入(1~5)!");
continue;
}
//读取数据
FileReader fr = new FileReader("D:\\Program Files (x86)\\MyStudy\\src\\postaddress\\input\\input_assign01_0"+num+".dat");
BufferedReader br = new BufferedReader(fr);
String s = null;
int size = Integer.parseInt(br.readLine());
//定义居民点坐标及对应的权重
int[] x = new int[size];
int[] y = new int[size];
double[] xweight = new double[size];
double[] yweight = new double[size];
int i = 0;
while((s = br.readLine()) != null){
String[] a = s.split(","); //输入的数据文件中坐标与权重用","分隔开
x[i] = Integer.parseInt(a[0]);
y[i] = Integer.parseInt(a[1]);
xweight[i] = Integer.parseInt(a[2]);
yweight[i] = Integer.parseInt(a[2]);
i++;
}
zuobiao(x, y, xweight, yweight);
System.out.println();
}
}
//快速排序
public static void qSort(int[] a, double[] weight, int low, int height){// a:待排序数组; weight:权值; low:待排序数组最低点; height 待排序数组最高点
int temp = 0;
double temp1 =0;
int i = low;
int j = height;
if(low < height){
temp = a[low];
temp1 = weight[low];
while(i != j){
while(j > i && a[j] >= temp){
--j;
}
if(i < j){
a[i] = a[j];
weight[i] = weight[j];
++i;
}
while(i < j && a[i] < temp){
++i;
}
if(i < j){
a[j] = a[i]; weight[j] = weight[i]; --j;
}
}
a[i] = temp;
weight[i] = temp1;
qSort(a, weight, low, i - 1);
qSort(a, weight, i + 1, height);
}
}
//对每个轴坐标进行快速排序,同时调整对应的权重
public static int axis(int []addr, double [] Weights, String zhou){
qSort(addr, Weights, 0, addr.length - 1);
System.out.println("排序后的" + zhou + "轴坐标为:");
for(int i = 0; i < addr.length; i++){
System.out.print(addr[i] + " ");
}
System.out.println("\n排序后的" + zhou + "轴坐标对应的权值为:");
for(int i = 0; i < Weights.length; i++){
System.out.print(Weights[i] + " ");
}
// 所有居民点权值之和
double sumweight = 0;
for(int i = 0; i < Weights.length; i++){sumweight += Weights[i]; }
System.out.println("\n所有居民点权值之和:\n" + sumweight);
//求带权中位数
for(int i = 0; i < Weights.length; i++){
sumweight += Weights[i];
if(sumweight >= sumweight / 2){ return addr[i]; }
}
return 0;
}
//求邮局坐标
public static void zuobiao(int[] Xaxis, int[] Yaxis, double [] XWeights, double[] YWeights){//Xaxis:x轴坐标点集; Yaxis:y轴坐标点集; XWeights:权重; YWeights:权重
int px = axis(Xaxis, XWeights, "X");//对x轴坐标点进行快速排序
int py = axis(Yaxis, YWeights, "Y");//对y轴坐标点进行快速排序
System.out.println("邮局位置为:\n(" + px + "," + py + ")");//打印满足条件的邮局位置
}
}
编程语言:Java
运行环境:Win10、MyEclipse
运行过程说明:根据提示信息输入要测试的数据文件的编号(1-5),数据文件中第一行为居民个数,后面的每行是居民位置坐标及权值,其中居民位置横、纵坐标、权值用","分隔(如:1,2,3)。输入数据文件的编号后程序开始运行,依次输出排序后的x、y轴坐标及对应权值,最后输出满足距离最小条件的邮局位置。
伪码描述:输入数据文件的编号;
利用FileReader、BufferedReader读取数据;
利用void qSort()对每个轴坐标进行快速排序,同时调整对应的权重;
求带权中位数:
for(int i = 0; i < Weights.length; i++){
sumweight += Weights[i];
if(sumweight >= sumweight / 2){
return addr[i]; }
}
打印邮局坐标
算法设计:实质上是带权中位数问题。用两个一维整型数组来分别定义x轴、y轴坐标取值,由于每个位置的代价不同,使用两个一维double数组来分别定义坐标的x轴、y轴的权重。先进行快速排序,同时依次调整每个点对应的权值,求出x、y轴的带权中位数坐标,最后打印出邮局坐标。
算法分析:快速排序的时间复杂度为O(nlogn)
求距离总和为O(n)
总的时间复杂度为O(nlogn)
运行情况:
input_assign01_01.dat中的数据为:
6
1,1,3
3,5,6
9,-5,9
7,-18,24
8,9,5
10,0,6