HDU 4311&4312 Meeting point-1&2 (曼哈頓距離&&切比雪夫距離)
題意:平面上有n個點,一個點(x,y)只能到達(x-1,y), (x+1,y), (x, y-1), (x, y+1)4個點。從n個點中找到一點,使其他點到此點的距離之和最小。
思路:
可以發現,兩個點間距離為 |x1-x2| + |y1-y2| ,這便是兩點間的曼哈頓距離。
樸素的做法是遍歷所有點,列舉該點與其他點間的曼哈頓距離之和,但是會TLE;
取巧的做法是將所有點與中心點的曼哈頓距離排序,列舉中間大概250個點左右的情況比較即可(不要欺負人家資料水!
正確姿勢:
用結構體陣列p[i]保持輸入資料x[i],y[i]
將點的x值升序排序求出前i項x和sum1[i],將點的y值升序排序求出前i項y和sum2[i].
求出p[i].x在排序後的x[]中的位置ox,p[i].y在排序後的y[]中的位置oy,則以p[i]為所選中心點的曼哈頓距離之和為
(x_[ox]*(ox-1)-sum1[ox-1]) + (sum1[n]-sum1[ox]-x_[ox]*(n-ox)) + (y_[oy]*(oy-1)-sum2[oy-1]) + (sum2[n]-sum2[oy]-y_[oy]*(n-oy));
這個公式的意思就是有ox-1個點的x值小於p[i],n-ox個大於p[i],故可以消去絕對值符號,將曼哈頓距離的前半部分之和求出來。後半部分同理。code:
/* * @author Novicer * language : C++/C */ #include<iostream> #include<sstream> #include<fstream> #include<vector> #include<list> #include<deque> #include<queue> #include<stack> #include<map> #include<set> #include<bitset> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cctype> #include<cmath> #include<ctime> #include<iomanip> #define INF 2147483647 #define cls(x) memset(x,0,sizeof(x)) #define rise(i,a,b) for(int i = a ; i <= b ; i++) using namespace std; const double eps(1e-8); typedef long long lint; const int maxn = 100000 + 5; int n; lint x_[maxn],y_[maxn]; lint sum1[maxn],sum2[maxn]; struct pt{ int x,y; }; pt p[maxn]; void input(){ cin >> n; for(int i = 1 ; i <= n ; i++){ scanf("%I64d%I64d",&x_[i], &y_[i]); p[i].x = x_[i]; p[i].y = y_[i]; } sort(x_+1,x_+n+1); cls(sum1); for(int i = 1 ; i <= n ; i++) sum1[i] = sum1[i-1] + x_[i]; sort(y_+1,y_+n+1); cls(sum2); for(int i = 1 ; i <= n ; i++) sum2[i] = sum2[i-1] + y_[i]; } void solve(){ lint ans = 1e18; for(int i = 1 ; i <= n ; i++){ lint ox = lower_bound(x_+1 , x_+n+1 , p[i].x) - (x_); lint oy = lower_bound(y_+1 , y_+n+1 , p[i].y) - (y_); lint tmp = 0; tmp += (x_[ox]*(ox-1)-sum1[ox-1]) + (sum1[n]-sum1[ox]-x_[ox]*(n-ox)); tmp += (y_[oy]*(oy-1)-sum2[oy-1]) + (sum2[n]-sum2[oy]-y_[oy]*(n-oy)); ans = min(ans , tmp); } cout << ans << endl; } int main(){ int t;cin >> t; while(t--) { input(); solve(); } return 0; }
題意:平面上有n個點,一個點(x,y)只能到達(x-1,y), (x+1,y), (x, y-1), (x, y+1), (x-1,y+1), (x-1,y-1), (x+1,y+1), (x+1,y-1)8個點。從n個點中找到一點,使其他點到此點的距離之和最小。
思路:可以發現,兩個點間距離為max(|x1-x2|, |y1-y2|),這便是是切比雪夫距離。
我們可以將座標軸順時針旋轉45度並將所有點的座標值放大sqrt(2)倍,
切比雪夫距離便是所得到的新座標系中的曼哈頓距離的二分之一。
然後按照上述題做法直接來即可。
假設有兩點(x1,y1), (x2,y2),不妨設 x1>x2。
則Chebyshev距離 D1 = max(|x1-x2|, |y1-y2|)
這兩個點對應到新座標系中的座標為 (x1-y1, x1+y1), (x2-y2, x2+y2)
某點繞原點逆時針旋轉α°(或座標軸順時針旋轉)後,點(x,y)的座標會變為(cosα x - sinα y , sinα x + cosα y)。
則Manhattan 距離D2 = |x1-y1-x2+y2| + |x1+y1-x2-y2|
分四種情況討論:
1.1 y1>y2 && x1-x2>y1-y2
D1 = max(x1-x2, y1-y2) = x1 - x2
D2 = x1-y1-x2+y2 + x1+y1-x2-y2 = 2(x1-x2)
1.2 y1>y2 && x1-x2<=y1-y2
D1 = max(x1-x2,y1-y2) = y1-y2
D2 = -(x1-y1-x2+y2) + x1+y1-x2-y2 = 2(y1-y2)
2.1 y1<=y2 && x1-x2>y2-y1
D1 = max(x1-x2, y2-y1) = x1-x2
D2 = x1-y1-x2+y2 + x1+y1-x2-y2 = 2(x1-x2)
2.2 y1<=y2 && x1-x2<=y2-y1
D1 = max(x1-x2, y2-y1) = y2-y1
D2 = x1-y1-x2+y2 - (x1+y1-x2-y2) = 2(y2-y1)
於是,將Chebyshev距離形式轉化成Manhattan距離的形式再求解即可。
code:
/*
* @author Novicer
* language : C++/C
*/
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#define INF 2147483647
#define cls(x) memset(x,0,sizeof(x))
#define rise(i,a,b) for(int i = a ; i <= b ; i++)
using namespace std;
const double eps(1e-8);
typedef long long lint;
const int maxn = 100000 + 5;
int n;
int x[maxn],y[maxn];
lint sumx[maxn],sumy[maxn];
lint s1,s2;
struct pt{
int x,y;
}p[maxn];
void input(){
cin >> n;
for(int i = 0 ; i < n ; i++){
scanf("%d%d",&x[i],&y[i]);
int tmp = x[i];
x[i] += y[i];
y[i] = tmp - y[i];
p[i].x = x[i];p[i].y = y[i];
}
}
void solve(){
sort(x,x+n);
sort(y,y+n);
cls(sumx);cls(sumy);
for(int i = 1 ; i <= n ; i++){
sumx[i] = sumx[i-1] + x[i-1];
sumy[i] = sumy[i-1] + y[i-1];
}
lint ans = 1e18;
for(int i = 0 ; i < n ; i++){
lint px = lower_bound(x , x+n , p[i].x) - x;
lint py = lower_bound(y , y+n , p[i].y) - y;
lint tmp = 0;
lint tmpx = (2*px - n)*p[i].x + sumx[n] - 2*sumx[px];
lint tmpy = (2*py - n)*p[i].y + sumy[n] - 2*sumy[py];
tmp = tmpx + tmpy;
ans = min(tmp , ans);
}
cout << ans / 2 << endl;
}
int main(){
// freopen("input.txt","r",stdin);
int t;cin >> t;
while(t--) {
input();
solve();
return 0;
}
ps.還有一種距離叫做尤拉距離,就是D = sqrt((x1- x2)^2 + (y1 - y2)^2)