题意:有n个点,求以这n个点中的某一点为起点,到各点的曼哈顿距离和最小是多少
分析:
暴力枚举又要超时,这种题一般都是考思维了,多半都是用技巧找到一个高效的方法。个人觉得这题跟上一篇文章的题是一个类型。这种思想要记住。
这题也是用“分治”,虽说题目要求的是曼哈顿距离,但是我们为什么真的就要一步到位的求呢,可以横纵坐标分开求,先x排序,然后遍历一遍,求出横坐标的距离,然后y排序,遍历一遍求出坐标的距离加在刚才求得的x的距离上,就是曼哈顿距离了。
这里有一个非常巧妙但是其实很显而易见的东西:假定现在我们已经按x排好序了分别是ABC三个点,那么C到AB的距离和是|C-A|+|C-B|,又因为已经排序了,那么绝对值可以去掉,得(C-A)+(C-B),那么就是2*C-(A+B),也就是说一个点到它前面的点的距离的和等于它前面的点的个数乘以它的自己再减去前面所有点的和,到这里你是不是想到求一个数列的和的时候我们遍历一遍数列就得到了,通过这个方法我们用O(n)就得到了一个点到它前面的点的距离和,然后再倒着遍历一遍数列用相似的思想把它到它后面的点的距离再加上就行了。
还有一种也是分开x,y排序求和,不过可以这么做:先直接求出最前面的点到所有点的距离,然后依次遍历数列,通过后面一个点跟前面一个点的关系,找出差值相减,就是了。不明白的话仔细想想或者画图看看。
代码:
#include<iostream>
#include<cstring>
#include<algorithm>
#define INF 1000000000000007
using namespace std;
struct node{
long long x,y;
long long sum;
}a[100005];
bool cmp1(node a,node b)
{
return a.x<b.x;
}
bool cmp2(node a,node b)
{
return a.y<b.y;
}
int main(){
int t,n;
cin>>t;
while(t--){
cin>>n;
long long ans=INF;
memset(a,0,sizeof(0));
for(int i=0;i<n;i++) cin>>a[i].x>>a[i].y;
sort(a,a+n,cmp1);
long long sum=0;
for(int i=0;i<n;i++){
a[i].sum=i*a[i].x-sum;
sum+=a[i].x;
}
sum=0;
for(int i=n-1;i>=0;i--){
a[i].sum+=sum-(n-1-i)*a[i].x;
sum+=a[i].x;
}
sort(a,a+n,cmp2);
sum=0;
for(int i=0;i<n;i++){
a[i].sum+=i*a[i].y-sum;
sum+=a[i].y;
}
sum=0;
for(int i=n-1;i>=0;i--){
a[i].sum+=sum-(n-1-i)*a[i].y;
sum+=a[i].y;
ans=min(ans,a[i].sum);
}
cout<<ans<<endl;
}
}