一、中位数问题
1. 问题模型
数轴(x)上有(n)个点,现在给出这(n)个点的坐标(a[i](iin [1,n])),让你选择一个点(k(kin [1,n])),使得每个点到点(k)的距离之和最小。
2. 结论
点(k)为序列的中位数时最优:
若n为奇数,点(k)位于a[(n+1)/2]处最优;若n为偶数,点(k)位于(a[n/2])或(a[n/2+1])均为
3. 推导
首先将(a[1])~(a[n])排序,假设点(p)选在坐标(q)处,点(p)左侧有(l)个点,点(p)右侧有(r)个点,此时分为两种情况:
(llt r),若点(p)向右移动单位距离,则距离之和减小((r-l))。
(rlt l),若点(p)向左移动单位距离,则距离之和减小((l-r))。
因此保证点p的左侧节点和右侧节点数量尽量相等时(即(l=r)),距离之和最优。
该结论同样适用于求出最优点(k),当点(k)的左右两侧节点数相等时为最优解,此时(k)就是序列的中位数。此时无论左移或右移点(k)都会使结果更差。
4. 例题
[CH0501]货仓选址
描述
在一条数轴上有N家商店,它们的坐标分别为 A[1]~A[N]。现在需要在数轴上建立一家货仓,每天清晨,从货仓到每家商店都要运送一车商品。为了提高效率,求把货仓建在何处,可以使得货仓到每家商店的距离之和最小。
输入格式
第一行一个整数N,第二行N个整数A[1]~A[N]。
输出格式
一个整数,表示距离之和的最小值。
样例输入
4
6 2 9 1
样例输出
12
数据范围与约定
对于100%的数据: N<=100000, A[i]<=1000000
显然,将货仓建于中位数处最优。
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,pos,a[100010];ll ans=0;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);
if(n&1) pos=(n+1)>>1;
else pos=n>>1;
for(int i=1;i<=n;i++)
ans+=abs(a[pos]-a[i]);
printf("%d",ans);
return 0;
}
二、带权中位数问题
1. 问题模型
数轴(x)上有(n)个点,现在给出这(n)个点的坐标(a[i](iin [1,n]))以及这(n)个点的点权(num[i]),让你选择一个点(k(kin [1,n])),使得每个点到点(k)的距离与点权的乘积之和最小。
2. 结论
满足(sum_{i=1}^{k} num[i]geq tot/2)时((tot)为所有点权之和)的最小点(k)为最优点。
3. 推导
可以把每个节点(i)由原来只有一个节点看做有(num[i])个点同时位于坐标(i)上,此时我们知道当点(k)位于(tot/2)的坐标上最优。
4. 例题
抗震救灾
问题描述
这场灾难发生后,国家决定设立研究所研究灾后重建工作,由全国各地派技术人员来参加。因为每个地区所派的技术人员数目不
同,出于节约经费的问题,所以目前还没有决定到底有在哪个地区设置研究所进行研究。假设所有地区都在一条直线上,现在
只知道每个地区与汶川的距离和该地派出技术人员的数目(假设汶川在最左端)。请你编程帮助他们确定在哪个地区建立研究
所可以使所有技术人员集中到该地区的费用总和最小。
输入格式
输入文件每一行描述一个地区的信息(地区数<=5000)。 对于每一行,首先是该地区派出的技术人员数目,紧跟着是这个地区相对于汶川的距离,最后是该地区的名称。(技术人员数<=100,地区的相对距离<=10^31,地区名称长度<=20,数据保证有唯一的解);
输出格式
输出文件只需一行,即研究所设定的地区名称。
Sample Input1
7 9289 shengyan
5 8523 beijing
3 5184 guilin
8 2213 chongqing
10 0 wuhan
Sample Output1
chongqing
Code:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n=1;
ll tot,sum=0;
struct node{
ll num,dis;
char name[50];
bool operator < (const node &other) const{
return dis<other.dis;
}
}p[5050];
int main()
{
while(scanf("%lld%lld%s",&p[n].num,&p[n].dis,p[n].name)!=EOF) tot+=p[n].num,n++;
n--;
sort(p+1,p+n+1);
for(int i=1;i<=n;i++){
sum+=p[i].num;
if(sum>=tot/2){
printf("%s
",p[i].name);
return 0;
}
}
return 0;
}