Bzoj 3170[Tjoi 2013]松鼠聚会 曼哈顿距离与切比雪夫距离
3170: [Tjoi 2013]松鼠聚会
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1318 Solved: 664
[Submit][Status][Discuss]
Description
有N个小松鼠,它们的家用一个点x,y表示,两个点的距离定义为:点(x,y)和它周围的8个点即上下左右四个点和对角的四个点,距离为1。现在N个松鼠要走到一个松鼠家去,求走过的最短距离。
Input
第一行给出数字N,表示有多少只小松鼠。0<=N<=10^5
下面N行,每行给出x,y表示其家的坐标。
-10^9<=x,y<=10^9
Output
表示为了聚会走的路程和最小为多少。
Sample Input
6
-4 -1
-1 -2
2 -4
0 2
0 3
5 -2
-4 -1
-1 -2
2 -4
0 2
0 3
5 -2
Sample Output
20
这道题貌似纯考小知识点吧……
不得不说做这道题挺长姿势的,科普一下:欧几里德距离,曼哈顿距离,切比雪夫距离(在这里博主不介绍在数学其他方面的定义,用途)。
欧几里德距离: 两点间的直线距离。(sqrt((x1-x2)^2+(y1-y2)^2))
曼哈顿距离(出租车几何):两个点在标准坐标系上的绝对轴距总和。(|x1-x2|+|y1-y2|)。
切比雪夫距离(棋盘距离):在国际象棋中国王到其他点的距离。(max(|x1-x2|,|y1-y2|))。
这三个距离各有各的用处,这道题主要涉及的是曼哈顿距离和切比雪夫距离的转化。
首先先明确一点,松鼠家之间的距离是切比雪夫距离,即我们先斜着走,在横着或竖着走,显而易见是最快的。
但是,这样我们只能写出n^2打法,过这道题还是不太可能。因此,我们需要一个神奇的东西:切比雪夫距离转曼哈顿距离。
设两个点为(x1,y1)(x2,y2)把两个点换成(x1+y1,x1-y1)(x2+y2,x2-y2)他们的曼哈顿距离除二就是(x1,y1)(x2,y2)的切比雪夫距离。
剩下的,我们利用前缀和就可以做到了。
至于证明,网上很多。
1 #include <iostream> 2 #include <cstdlib> 3 #include <cstdio> 4 #include <cstring> 5 #include <string> 6 #include <queue> 7 #include <algorithm> 8 #include <cmath> 9 #include <map> 10 #define N 100005 11 using namespace std; 12 int n; 13 struct no 14 { 15 long long x,y,bh; 16 }node[N]; 17 long long ans[N],sumx[N],sumy[N]; 18 bool px1(no a,no b) 19 { 20 if(a.x==b.x)return a.y<b.y; 21 return a.x<b.x; 22 } 23 bool px2(no a,no b) 24 { 25 if(a.y==b.y)return a.x<b.x; 26 return a.y<b.y; 27 } 28 int main() 29 { 30 scanf("%d",&n); 31 for(int i=1;i<=n;i++) 32 { 33 int x,y; 34 scanf("%d%d",&x,&y); 35 node[i].x=x+y,node[i].y=x-y; 36 node[i].bh=i; 37 } 38 sort(node+1,node+1+n,px1); 39 for(int i=1;i<=n;i++) sumx[i]=node[i].x+sumx[i-1]; 40 41 for(long long i=1;i<=n;i++) 42 { 43 ans[node[i].bh]+=i*node[i].x-sumx[i]+sumx[n]-sumx[i]-(n-i)*node[i].x; 44 } 45 sort(node+1,node+1+n,px2); 46 for(int i=1;i<=n;i++)sumy[i]=node[i].y+sumy[i-1]; 47 for(long long i=1;i<=n;i++) 48 { 49 ans[node[i].bh]+=i*node[i].y-sumy[i]+sumy[n]-sumy[i]-(n-i)*node[i].y; 50 51 } 52 53 long long an=1000000000ll*1000000000ll; 54 for(int i=1;i<=n;i++) 55 { 56 if(ans[i]/2<an)an=ans[i]/2; 57 } 58 printf("%lld\n",an); 59 return 0; 60 }