一. 实验题目
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;
}*/