最小距离之和-中位数

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
第1行:点的数量N。(2 <= N <= 10000)
第2 - N + 1行:点的位置。(-10^9 <= P[i] <= 10^9)
代码:根据就是中位数
#include <iostream>
#include <algorithm>
using namespace std;
int a[10010];
int main()
{
	int N;
	while (cin >> N)
	{
		for (int i = 0; i < N; ++ i)
		{
			cin >> a[i];
		}
		sort(a,a+N);
		int l = 0,r = N - 1;
		__int64 sum = 0;
		while (l <= r)
		{
			sum += a[r --] - a[l ++];
		}
		cout << sum << endl;
	}
	return 0;
}

3.2 距离之和最小V2

三维空间上有N个点, 求一个点使它到这N个点的曼哈顿距离之和最小,输出这个最小的距离之和。
点(x1,y1,z1)到(x2,y2,z2)的曼哈顿距离就是|x1-x2| + |y1-y2| + |z1-z2|。即3维坐标差的绝对值之和。
Input
第1行:点的数量N。(2 <= N <= 10000)
第2 - N + 1行:每行3个整数,中间用空格分隔,表示点的位置。(-10^9 <= X[i], Y[i], Z[i] <= 10^9)
代码:3.1的变形,将x,y,z分开求,每一个就和3.1一样了,然后再求和即可。
#include <iostream>
#include <algorithm>
using namespace std;

__int64 getMinDis(int a[],int N)
{
	sort(a,a+N);
	int l = 0,r = N - 1;
	__int64 sum = 0;
	while (l <= r)
	{
		sum += a[r --] - a[l ++];
	}
	return sum;
}
int main()
{
	int N;
	while (cin >> N)
	{
		int x[10002],y[10002],z[10002];
		for (int i = 0; i < N; ++ i)
		{
			cin >> x[i] >> y[i] >> z[i];
		}
		
		cout << getMinDis(x,N)+getMinDis(y,N)+getMinDis(z,N) << endl;
	}
	return 0;
}


3.2 距离之和最小V3

X轴上有N个点,每个点除了包括一个位置数据X[i],还包括一个权值W[i]。该点到其他点的带权距离 = 实际距离 * 权值。求X轴上一点使它到这N个点的带权距离之和最小,输出这个最小的带权距离之和。
Input
第1行:点的数量N。(2 <= N <= 10000)
第2 - N + 1行:每行2个数,中间用空格分隔,分别是点的位置及权值。(-10^5 <= X[i] <= 10^5,1 <= W[i] <= 10^5)
代码:这个就是求出带权中位数,然后又和3.1是一样的。关键是带权中位数如何求,就是
1.1 按照从小到大的顺序给x1……xn排序
 1.2 遍历数组,直至找到第一个xk,满足sigma(wi)(xi<xk)>=W/2,则xk就是要找的带权中位数。
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;

const int MAXN = 10010;
typedef struct NODE
{
	__int64 x;
	__int64 w;
}Node;
Node node[MAXN];

bool cmp(const Node &a, const Node &b)
{
	return a.x < b.x;
}
int main()
{
	int N;
	while (cin >> N)
	{
		__int64 wSum = 0;
		for (int i = 0; i < N; ++ i)
		{
			cin >> node[i].x >> node[i].w;
			wSum += node[i].w;
		}
		sort(node,node+N,cmp);
		wSum /= 2;
		__int64 tmp = 0;
		int pos = 0;
		for (int i = 0; i < N; ++ i)
		{
			tmp += node[i].w;
			if (tmp >= wSum)
			{
				pos = i;
				break;
			}
		}
		__int64 sum = 0;
		
		for (int i = 0; i < N; ++ i)
		{
			if (i <= pos)
			{
				sum += (node[pos].x - node[i].x)*node[i].w;
			}
			else
			{
				sum += (node[i].x - node[pos].x)*node[i].w;
			}
		}
		cout << sum << endl;
	}
	return 0;
}


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

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值