51nod 最小距离之和-中位数

本文介绍了带权中位数的概念及其性质,带权中位数满足使得权重加权的距离和最小的条件。通过三个例题及变形,包括原始的距离之和最小问题以及两个变种,详细展示了如何运用排序和迭代找到最小距离和的解决方案。
摘要由CSDN通过智能技术生成

1 中位数

 中位数:将数组大小为n的数据,从大到小,或者是从小到大排列,那么当n为奇数的时候,中位数就是(n+1)/2的这个数,当n为偶数的时候,中位数就是n/2和(n+1)/2这二个数据的平均数。
中位数:也就是选取中间的数。一种衡量集中趋势的方法。
要找中位数,首先需要从小到大排序,例如这组数据:23、29、20、32、23、21、33、25;
我们将数据排序20、21、23、23、25、29、32、33;排序后发现有8个数怎么办?
若有n个数,则选择第(n+1)/2个(当n为奇数),或选择(n/2+1)个(当n为偶数)
此例中选择25为中位数

2 带权中位数

带权中位数:首先也是将这个数组的数据按一定的顺序排列, 带权中位数(Weighted Median)对于n个互不相同的元素集合x1、x2……xn,其权重依次为w1、w2……wn。令W = sigma(wi),则带权中位数xk满足:(这里的权重可以用这个数据出现的频率来表示,或者这个数据的重要性)
    sigma(wi)(xi<xk)<=W/2  

    sigma(wi)(xi>xk)<=W/2
其中sigma表示求和。

带权中位数满足:sigma(|xi-xk|*wi)最小

百度的证明:

D[I]为权重,DIST为距离

若最优点在T
则有:
{D[I]*DIST(I,T)}(I<>T)<={D[I]*DIST(I,T+1)}(I<>T+1)
将此式化为:
{D[L]}*DIST(L,T)}+{D[R]*DIST(R,T)}+D[T+1]*DIST(T+1,T)
<=∑ {D[L]}*DIST(L,T+1)}+∑ {D[R]*DIST(R,T+1)}+D[T]*DIST(T,T+1) (L<T&R>T+1)
即:
{D[L]*DIST(L,T+1)}-{D[L]*DIST(L,T)}(L<T)+D[T]*(DIST(T,T+1))>={D[R]*DIST(R,T)}-(D[R]*DIST(R,T+1))(R>T+1)+D[T+1]*(DIST(T,T+1))进一步化简为:
{D[L]*(DIST(L,T)-DIST[L,T+1])}(L<=T)<={D[R]*(DIST(R,T+1)-DIST(R,T))}(R>=T+1)DIST(L,T)-DIST(L,T+1)=DIST(T,T+1)
DIST(R,T+1)-DIST(R,T)=DIST(T+1,T)
OBVIOUSLY : DIST(T,T+1)=DIST(T+1,T)
因此:
D[L](L<=T)>=(D[R])(R>=T+1)
即:∑ D[L](L<T)+D[T]>=(D[R])(R>T)
因此我们发现,若 T是最优点,则必有其左边的权值和加上 D[T]后大于右边的权值和
而类似的,我们可以证明其右边的权值和加上 D[T]后大于左边的权值和
因此我们要找的点也就是满足以上条件的点。注意到此时我们的选择已经和具体的位置(坐标)没有关系了,而成为主要考虑因素的仅仅是各点上的权值。
因为左边的权值和数+ D[T]>=右边的权值和,那么:
LEFTSUM+D[T]>= RIGHTSUM= SUMALL-(LEFTSUM+D[T])
=>2*(LEFTSUM+D[T])>= SUMALL
=>2*RIGHTSUM<= SUMALL
同理可得:
RIGHTSUM+D[T]>= LEFTSUM= SUMALL-(RIGHTSUM+D[T])
=>2*(RIGHTSUM+D[T])>= SUMALL
=>2*LEFTSUM<= SUMALL
此时我们发现:
2*LEFTSUM<= SUMALL 而  2*(LEFTSUM+D[T])>= SUMALL
也即是说当前的位置 T上的数包含了第[( SUMALL)/2]个数,由开篇的简述可知,这第[( SUMALL)/2]个数,就是这个序列中的带权中位数。所以这一类问题,实质上就是带权中位数问题。

3 例题及变形

3.1 距离之和最小

X轴上有N个点,求X轴上一点使它到这N个点的距离之和最小,输出这个最小的距离之和。
Input
<span id="Showjim86_bnbbbsbl_s35"></span>第1行:点的数量N。(2 <= N <= 10000)
第2 - N + 1行:点的位置。(-10^9 <= P[i] <= 10^9)
代码:根据就是中位数
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4. int a[10010];  
  5. int main()  
  6. {  
  7.     int N;  
  8.     while (cin >> N)  
  9.     {  
  10.         for (int i = 0; i < N; ++ i)  
  11.         {  
  12.             cin >> a[i];  
  13.         }  
  14.         sort(a,a+N);  
  15.         int l = 0,r = N - 1;  
  16.         __int64 sum = 0;  
  17.         while (l <= r)  
  18.         {  
  19.             sum += a[r --] - a[l ++];  
  20.         }  
  21.         cout << sum << endl;  
  22.     }  
  23.     return 0;  
  24. }  

3.2 距离之和最小V2

三维空间上有N个点, 求一个点使它到这N个点的曼哈顿距离之和最小,输出这个最小的距离之和。
点(x1,y1,z1)到(x2,y2,z2)的曼哈顿距离就是|x1-x2| + |y1-y2| + |z1-z2|。即3维坐标差的绝对值之和。
Input
<span id="Showjim86_bnbbbsbl_s216"></span>第1行:点的数量N。(2 <= N <= 10000)
第2 - N + 1行:每行3个整数,中间用空格分隔,表示点的位置。(-10^9 <= X[i], Y[i], Z[i] <= 10^9)<span id="Showjim86_bnbbbsbl_e216"></span>
代码:3.1的变形,将x,y,z分开求,每一个就和3.1一样了,然后再求和即可。
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. using namespace std;  
  4.   
  5. __int64 getMinDis(int a[],int N)  
  6. {  
  7.     sort(a,a+N);  
  8.     int l = 0,r = N - 1;  
  9.     __int64 sum = 0;  
  10.     while (l <= r)  
  11.     {  
  12.         sum += a[r --] - a[l ++];  
  13.     }  
  14.     return sum;  
  15. }  
  16. int main()  
  17. {  
  18.     int N;  
  19.     while (cin >> N)  
  20.     {  
  21.         int x[10002],y[10002],z[10002];  
  22.         for (int i = 0; i < N; ++ i)  
  23.         {  
  24.             cin >> x[i] >> y[i] >> z[i];  
  25.         }  
  26.           
  27.         cout << getMinDis(x,N)+getMinDis(y,N)+getMinDis(z,N) << endl;  
  28.     }  
  29.     return 0;  
  30. }  
  31.   

3.2 距离之和最小V3

X轴上有N个点,每个点除了包括一个位置数据X[i],还包括一个权值W[i]。该点到其他点的带权距离 = 实际距离 * 权值。求X轴上一点使它到这N个点的带权距离之和最小,输出这个最小的带权距离之和。
Input
<span id="Showjim86_bnbbbsbl_s216"></span>第1行:点的数量N。(2 <= N <= 10000)
第2 - N + 1行:每行2个数,中间用空格分隔,分别是点的位置及权值。(-10^5 <= X[i] <= 10^5,1 <= W[i] <= 10^5)<span id="Showjim86_bnbbbsbl_e216"></span>
代码:这个就是求出带权中位数,然后又和3.1是一样的。关键是带权中位数如何求,就是
1.1 按照从小到大的顺序给x1……xn排序
 1.2 遍历数组,直至找到第一个xk,满足sigma(wi)(xi<xk)>=W/2,则xk就是要找的带权中位数。
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <algorithm>  
  3. #include <cmath>  
  4. using namespace std;  
  5.   
  6. const int MAXN = 10010;  
  7. typedef struct NODE  
  8. {  
  9.     __int64 x;  
  10.     __int64 w;  
  11. }Node;  
  12. Node node[MAXN];  
  13.   
  14. bool cmp(const Node &a, const Node &b)  
  15. {  
  16.     return a.x < b.x;  
  17. }  
  18. int main()  
  19. {  
  20.     int N;  
  21.     while (cin >> N)  
  22.     {  
  23.         __int64 wSum = 0;  
  24.         for (int i = 0; i < N; ++ i)  
  25.         {  
  26.             cin >> node[i].x >> node[i].w;  
  27.             wSum += node[i].w;  
  28.         }  
  29.         sort(node,node+N,cmp);  
  30.         wSum /= 2;  
  31.         __int64 tmp = 0;  
  32.         int pos = 0;  
  33.         for (int i = 0; i < N; ++ i)  
  34.         {  
  35.             tmp += node[i].w;  
  36.             if (tmp >= wSum)  
  37.             {  
  38.                 pos = i;  
  39.                 break;  
  40.             }  
  41.         }  
  42.         __int64 sum = 0;  
  43.           
  44.         for (int i = 0; i < N; ++ i)  
  45.         {  
  46.             if (i <= pos)  
  47.             {  
  48.                 sum += (node[pos].x - node[i].x)*node[i].w;  
  49.             }  
  50.             else  
  51.             {  
  52.                 sum += (node[i].x - node[pos].x)*node[i].w;  
  53.             }  
  54.         }  
  55.         cout << sum << endl;  
  56.     }  
  57.     return 0;  
  58. }  
  59.   

上面都是nlogn的解法,算法导论又最坏为O(N)的解法的伪代码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值