邮局选址问题-带权中位数

一、运行环境:
Win7、MyEclipse、JDK8

二、运行过程说明:
数据文件格式:第一行是居民数量n的取值,第2~n+1行是每家的位置及权重。

输入格式:输入测试数据集文件编号,范围:1~6。

输出:
排序后的X轴坐标
排序后的X轴坐标对应的权值
所有居民点权值之和
排序后的Y轴坐标
排序后的Y轴坐标对应的权值为
所有居民点权值之和
邮局位置

三、算法设计

  1. 问题描述:

    在一个按照东西和南北方向划分成规整街区的城市里,n个居民点散乱地分布在不同的街区中。用x坐标表示东西向,用y坐标表示南北向。各居民点的位置可以由坐标(x,y)表示。要求:为建邮局选址,使得n个居民点到邮局之距离的总和最小。

  2. 解题思路:

带权中位数,就是给定的n个有序的数,每个数都有一个权值di。此时的中位数就不再是第n/2个数了,而是这个数 (∑_(i=1)n▒〖d_i〗 )/2。中位数可以看做是权值为1的带权中位数。最优位置的选择与距离无关,设最优位置在t,则有t左面的权值和加上d[t]后大于右面的权值和,而类似的也有右面的权值和加上d[t]后大于左面的权值和。
因此要找到的点也就是满足以上条件的点。此时选择已经和具体的位置(坐标)没有关系了,成为主要考虑因素的仅仅是各点上的权值。最优位置上的数,就是这个序列中的带权中位数,所以这类问题,实质上就是带权中位数的问题。
设邮局地址为(x_0,y_0),则
第i个居民点到邮局距离为:d_i=√(〖(x_i-x_0)〗2+〖(y_i-y_0)〗2 ),
n个居民点到邮局的带权距离的总和:d= ∑_(i=1)^n▒〖w_i*d_i 〗,
那么距离最小求解公式为:d_min=Min(∑_(i=1)n▒〖w_i*√(〖(x_i-x_0)〗2+〖(y_i-y_0)〗^2 )〗)。
对n个城市的x和y坐标分别排序,分别求出排序后的横坐标带权中位数和纵坐标带权中位数,即为邮局的最佳位置。

  1. 数据结构的选择:
    由上述分析可知:
    可以使用两个一维整型数组来分别定义坐标的x轴,y轴取值。另外,由于每个位置的代价不同,所以可以使用两个一维double数组来分别定义坐标的x轴,y轴的权重。
    三个方法:
  • 快速排序(同时将每个点对应的权值依次做调整)

    public static void kuaipai(int[] a, double[] weight, int low, int height)
    a[]:待排序数组(轴上点坐标),weight[]:权值,param low:待排序数组最低点,param height:待排序数组最高点。

  • 对每个轴求带权中位数对应的坐标
    public static int axis(int [] addr, double [] Weights, String zhou)
    addr:轴上坐标,Weights:权重,zhou:X或Y轴标识,return 带权中位数。

  • 求邮局坐标

    public static void zuobiao(int[] Xaxis, int[] Yaxis, double [] XWeights, double[] YWeights)
    Xaxis: x轴坐标点集,Yaxis :y轴坐标点集,Xweights:权重,Yweights:权重

四、算法详细描述:
步骤:

  • 选择测试文件的序号

  • 读取数据

  • 定义居民点坐标以及每个点对应的权重

  • 对x轴排序处理

  • x轴相对应的权值随着轴做对应调整,使其保持一一对应

  • 求x所有居民点权值之和

  • 求x轴方向的带权中位数 对y轴排序处理

  • y轴相对应的权值随着轴做对应调整,使其保持一一对应

  • 求y轴所有居民点权值之和 求y轴方向的带权中位数

  • 打印邮局位置

代码:

import java.util.Scanner;
import java.io.*;
public class Postaddress {
	public static void main(String[] args) throws Exception {
		while(true){
			//选择测试文件de编号
			System.out.println("共六组测试数据(1~6),请输入数据编号:");
			Scanner sc = new Scanner(System.in);
			int num = sc.nextInt();
			if(num<=0 || num>=7){
				System.out.println("编号错误,请重新输入!(1~6)");
				continue; }
			//读取数据
			BufferedReader br = new BufferedReader(new FileReader("./src/input_assgin01_0" + num + ".dat"));
			String s = null;
			int size = Integer.parseInt(br.readLine());
			 //1 定义居民点坐标以及每个点对应的权重
			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();
		}
	}
	/**
	 * 快速排序(同时将每个点对应的权值依次做了调整)
	 * @param a  待排序数组(轴上点坐标)
	 * @param weight  权值
	 * @param low  待排序数组最低点
	 * @param height  待排序数组最高点
	 */
	public static void kuaipai(int[] a, double[] weight, int low, int 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;
			kuaipai(a, weight, low, i - 1);
			kuaipai(a, weight, i + 1, height);
		}
	}
	/**
	 * 对每个轴求带权中位数对应的坐标
	 * @param addr  轴上坐标
	 * @param Weights  权重
	 * @param zhou  X或Y轴标识
	 * @return 带权中位数
	 */
	public static int axis(int [] addr, double [] Weights, String zhou){
		// 对每个轴坐标进行快速排序,同时调整对应的权重
		kuaipai(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] + " ");}
		//3 所有居民点权值之和	
		double sumweight = 0;
		for(int i = 0; i < Weights.length; i++){sumweight += Weights[i]; }
		System.out.println("\n所有居民点权值之和:\n" + sumweight);
		//4 求x轴方向的带权中位数
		double sum = 0;
		for(int i = 0; i < Weights.length; i++){
			sum += Weights[i];
			if(sum >= sumweight / 2){ return addr[i]; }
		}
		return 0;
	}
	/**
	 * 求邮局坐标
	 * @param Xaxis  x轴坐标点集
	 * @param Yaxis  y轴坐标点集
	 * @param XWeights  权重
	 * @param YWeights  权重
	 */
	public static void zuobiao(int[] Xaxis, int[] Yaxis, double [] XWeights, double[] YWeights){
		 //2 对x轴,y轴分别处理轴排完序相对应权值随着做调整
		 //2.1 对x轴坐标点进行快速排序
		int px = axis(Xaxis, XWeights, "X");
		//2.2 对y轴坐标点进行快速排序
		int py = axis(Yaxis, YWeights, "Y");
		//打印邮局位置
		System.out.println("邮局位置为:\n(" + px + "," + py + ")");
	}
}

五、算法分析
快排时间复杂度:O(nlogn),求距离和:O(n),总复杂度:O(nlogn),空间复杂度为O(n)。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值