邮局选址问题

问题描述:

    在一个按照东西和南北方向划分成规整街区的城市里,n个居民点散乱地分布在不同的街区中。用x 坐标表示东西向,用y坐标表示南北向。各居民点的位置可以由坐标(x,y)表示。街区中任意两点(x1,y1)和(x2,y2)之间的距离可以用数值|x1-x2|+|y1-y2|来度量。

邮局选址问题是,居民们希望在城市中选择建立邮局的最佳位置,使n个居民点到邮局的距离总和最小。

 

算法思想:

       设邮局的位置为(x,y).那么所有居民点东西向距邮局x的距离和sumX=|x-x1|+|x-x2|+…+|x-xn|。由中位数定理可知,x为所有居民点东西向坐标的中位数。同理可知,y为所有居民点南北向坐标的中位数。所以,问题就转化为求一个无序数组的中位数(高效求解:线性时间选择)。

      线性时间选择问题:给定线性集中n个元素和一个整数k,求这n个元素的第k小的元素,当k=(n+1)/2时,称为找中位数。

      如果能在线性时间内找到一个划分基准,使得按这个基准划分出的两个子数组的长度都至少为原数组长度的ε倍(0<ε<1),那么在最坏情况下用O(n)时间可以完成选择任务。

      划分基准的选择步骤:

      第一,将n个元素划分成⌈n/5⌉,每组5个元素,最后一组可能少于5个元素。对每组的元素进行排序,并取出每组元素的中位数,共⌈ n/5⌉个。

      第二,递归调用该函数,求⌈ n/5⌉个中位数的中位数,如果⌈ n/5⌉为偶数,求两个中间数较大的一个,以此作为基准进行划分。

      找到基准后,可以以此基准进行一趟快速排序,将数组划分成两个子数组。然后比较左边子数组个数与整数k的大小,确定第k小元素在哪一个子数组中,达到减小问题规模的目的。最后,递归调用该函数,求出第k小元素。

 

代码展示:

void Swap(int &a, int &b){
	int temp = a;
	a = b;
	b = temp;
}

//当元素较少时,可以选择冒泡排序求解中位数
void BubbleSort(int *a, int p, int r){
	int change = 1;
	for(int i = r;i>p && change;--i){
		change = 0;
		for(int j = p;j<i;j++){
			if(a[j]>a[j+1]){
				Swap(a[j],a[j+1]);
				change = 1;
			}
		}
	}
}

//快速排序
int QuickSort(int *a, int p, int r, int partition){
	int i = p-1, j = r+1;
	while(1){
		while(a[++i] < partition && i < r);
		while(a[--j] > partition);
		if(i >= j) break;
		Swap(a[i],a[j]);
	}
	a[j] = partition;
	return j;
}

//求第k小元素
int Select(int *a, int p, int r, int k){
	if(r-p<75){    //元素个数小于75,使用冒泡排序
		BubbleSort(a,p,r);
		return a[p+k-1];
	}
	for(int i = 0;i<=(r-p-4)/5;i++){
		BubbleSort(a,p+i*5,p+i*5+4);    //先把每组的5个元素排好序
		Swap(a[p+i],a[p+i*5+2]);        //将每组的中位数交换到数组的前面
	}
	int x = Select(a, p, p+(r-p-4)/5, p+(r-p-4)/10); //求解每组中位数的中位数x
	int i = QuickSort(a,p,r,x),j = i-p+1;    //以x作为快排的基准,达到成倍减小数组规模的目的
	if(k<=j){
		return Select(a,p,r,k);
	}else{
		return Select(a,i+1,r,k-j);
	}
}

运行结果:(可以使用随机数产生测试数据)

中间数据省略……

  • 5
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值