题意:给n(<=5e4)个点的坐标(位于同一平面),求最远点对的距离的平方。
分析:求凸包。
大概有 暴力法,分治法,步进法,graham扫描法等。
常用graham扫描法。
旋转卡壳法: 可以用来求凸包的直径(即最远的两点间的距离)
挑战程序设计P261-263
AC代码:
#include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <vector> #include <algorithm> using namespace std; #define mem(a,n) memset(a,n,sizeof(a)) #define memc(a,b) memcpy(a,b,sizeof(b)) #define rep(i,a,n) for(int i=a;i<n;i++) ///[a,n) #define dec(i,n,a) for(int i=n;i>=a;i--)///[n,a] #define pb push_back #define fi first #define se second #define IO ios::sync_with_stdio(false) #define fre freopen("in.txt","r",stdin) #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 typedef long long ll; typedef unsigned long long ull; const double PI=acos(-1.0); const double E=2.718281828459045; const double eps=1e-3; const int INF=0x3f3f3f3f; const int MOD=258280327; const int N=5e4+5; const ll maxn=1e6+5; const int dir[4][2]= {-1,0,1,0,0,-1,0,1}; struct point { double x,y; point() {} point(double x,double y):x(x),y(y) {} point operator + (point p) { return point(x+p.x,y+p.y); } point operator - (point p) { return point(x-p.x,y-p.y); } point operator * (double d) { return point(x*d,y*d); } ///点乘 double dot(point p) { return x*p.x+y*p.y; } ///叉乘 double det(point p) { return x*p.y-y*p.x; } bool operator < (const point& m)const { if(x!=m.x) return x<m.x; return y<m.y; } }; bool cmp(const point& a,const point& b) { if(a.x!=b.x) return a.x<b.x; return a.y<b.y; } ///求凸包 graham扫描法 vector<point> convex_hull(point *a,int tot) { sort(a,a+tot); vector<point>p(tot*2); ///构造中的凸包 int k=0;///凸包的顶点数 ///构造凸包的下侧 for(int i=0; i<tot; i++) { while(k>1&&(p[k-1]-p[k-2]).det(a[i]-p[k-1])<=0) k--; p[k++]=a[i]; } int t=k; ///构造凸包的上侧 for(int i=tot-2; i>=0; i--) { while(k>t&&(p[k-1]-p[k-2]).det(a[i]-p[k-1])<=0) k--; p[k++]=a[i]; } p.resize(k-1); return p; } ///距离的平方 double dis(point p,point q) { return (p-q).dot(p-q); } point a[N]; int n; void solve() { vector<point> p = convex_hull(a, N); int n = p.size(); if (n == 2) ///特别处理凸包退化的情况 { printf("%.0f\n", dis(p[0], p[1])); return; } int i = 0, j = 0; ///某个方向上的对踵点对 ///求出x轴方向上的对踵点对 for (int k = 0; k < n; k++) { if (!cmp(p[i], p[k])) i = k; if (cmp(p[j], p[k])) j = k; } double res = 0; int si = i, sj = j; while (i != sj || j != si) ///将方向逐步旋转180度 { res = max(res, dis(p[i], p[j])); ///判断先转到边i-(i+1)的法线方向还是边j-(j+1)的法线方向 if ((p[(i + 1) % n] - p[i]).det(p[(j + 1) % n] - p[j]) < 0) i = (i + 1) % n; ///先转到边i-(i+1)的法线方向 else j = (j + 1) % n; ///先转到边j-(j+1)的法线方向 } printf("%.0f\n", res); } int main() { scanf("%d",&n); for(int i=0; i<n; i++) scanf("%lf%lf",&a[i].x,&a[i].y); solve(); return 0; }
另外:较为清晰和简洁的写法:
http://hzwer.com/4224.html
http://www.cppblog.com/staryjy/archive/2009/11/19/101412.html