线性时间选择算法的分治思想:邮局选址问题和士兵战队问题

一. 实验题目

1. 邮局选址问题

在一个按照东西和南北方向划分成规整街区的城市里,n 个居民点散乱地分
布在不同的街区中。用 x 坐标表示东西向,用 y 坐标表示南北向。各居民点的
位置可以由坐标(x,y) 表示。街区中任意 2 点(x1,y1) 和(x2,y2) 之间的距离可以用
数值|x1-x2|+|y1-y2| 度量。 居民们希望在城市中选择建立邮局的最佳位置,使
n 个居民点到邮局的距离总和最小。
给定 n 个居民点的位置,编程计算 n 个居民点到邮局的距离总和的最小值。
【输出格式】: 输入由多组数据组成。
每组数据输入的第 1 行是居民点数 n,1≤n≤10000。接下来 n 行是居民点
的位置,每行 2 个整数 x 和 y,-10000≤x,y≤10000。
【输出格式】: 对应每组输入,输出的第 1 行中的数是 n 个居民点到邮局
的距离总和的最小值。
【 样 例 】
标准输入 标准输出 .
5 10
1 2
2 2
1 3
3 -2
3 3

2. 士兵站队问题

在一个划分成网格的操场上,n 个士兵散乱地站在网格点上。网格点用整数坐标(x,y)表示。士兵们可以沿网格边往上、下、左、右移动一步,但在同一时刻任一网格点上只能有一名士兵。按照军官的命令,士兵们要整齐地列成一个水平队列,即排列成(x,y),(x+1,y),…,(x+n-1,y)。如何选择 x 和 y 的值才能使士兵们以少的总移动步数排成一行。编程计算使所有士兵排成一行需要的最少移动步数。【输入格式】:第 1 行是士兵数 n,1≤n≤10000。接下来 n 行是士兵的初始位置,每行有 2 个整数 x 和 y,-10000≤x,y≤10000。
**【输出格式】:**一个数据,即士兵排成一行需要的最少移动步数。
【 样 例 】
标准输入 标准输出 .
5 8
1 2
2 2
1 3
3 -2
3 3

二. 算法思想

输油管道问题的分治思想得出的经验我们可以归纳如下:数轴上n个点到数轴上某点距离之和最短的点为中点,而在线性时间下求中点的一般方法为求第k个最小数的位置,此时k取n/2。具体的分治思想,请戳上面的那个链接。
所以:

第一题的思路,点的横坐标点和纵坐标中点即为邮局坐标,然后分别求距离和。

第二题思路,最终y不用说肯定是纵坐标的中点,最终的第一个士兵的x我们假设就为x,则士兵的初始横坐标值从左到右我们假设依次为 a0,a1,a2…an-1;所以这些士兵依次移动的距离为,|a0-x| +|a1-1-x|(a1先移动到a0,再移动x) +…+|an-1-(n-1)-x|;因此我们将a0-0,a1-1,…,ai - i 看成一个新的数组,因此,问题就右变成了求这个新的数组中位数的问题。这样就很容易求解了。
在这里插入图片描述

三. 代码:


#include<stdlib.h>//绝对值函数
#include<stdio.h>
#define N 10000
void Swap(int &a,int &b)
{
	int t=a;
	a=b;
	b=t;
}

int Partion (int a[],int l,int h)//a数组,l表示下限,h表示上限
{
	 int x=a[l];
	//将小于x的元素交换到左边区域,将大于x的元素交换的右边区域
	while(l<h)
	{
		while(l<h&&a[h]>=x) --h;
		Swap(a[l],a[h]);
		while(l<h&&a[l]<=x) ++l;
		Swap(a[l],a[h]);
	}
	a[l]=x;
	return l;//返回数轴的位置
}

int Select(int a[],int l,int h,int k)
{
	if(l==h) return a[l];
	int i=Partion(a,l,h);
	int j=i-l+1; //表示这个数轴是整个数组中第几小的.
	if(k<=j)//如果比第k小的数要大,则在比它小的数中中在找第k小的
	   return Select(a,l,i,k);
	else//如果比第k小的数要大,则只需要在比它大的数中找,第k-j大的
		return Select(a,i+1,h,k-j);

}
//问题1;
int main()
{
	int a[N],b[N];
    int n,mida,midb,sum=0;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
        scanf("%d %d",&a[i],&b[i]);
    mida=Select(a,0,n-1,n/2+1);
	midb=Select(b,0,n-1,n/2+1);
	
    for(int i=0; i<n; i++)
        sum+=abs(a[i]-mida)+abs(b[i]-midb);
    printf("%d",sum);
    return 0;
}
//问题2的求第k小的方法和上面相同。

/*int main()
{
	int a[N],b[N];
    int n,mida,midb,sum=0;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
        scanf("%d %d",&a[i],&b[i]);
    mida=Select(a,0,n-1,n/2+1);
	midb=Select(b,0,n-1,n/2+1);
	int c[N],midc;
	for(int i=0; i<n; i++)
        c[i]=b[i]-i;
	midc=Select(c,0,n-1,n/2+1);

    for(int i=0; i<n; i++)
        sum+=abs(a[i]-mida)+abs(c[i]-midc);
    printf("%d",sum);
    return 0;
}*/





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值