1. 程式人生 > >!HDU 4311 最小曼哈頓距離-思維&卡時間-(橫縱座標分開算,排序)

!HDU 4311 最小曼哈頓距離-思維&卡時間-(橫縱座標分開算,排序)

題意:有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;
	}
}