1. 程式人生 > >2018.10.03 bzoj3707: 圈地(計算幾何)

2018.10.03 bzoj3707: 圈地(計算幾何)

傳送門 計算幾何好題。

本蒟蒻表示不看題解只會O(n3)O(n^3)。 正解是先考慮把直線按照斜率從小到大排序,然後把點按座標排序。 這樣每次列舉到直線(a,b)(a,b)時,離直線a,ba,b最近的點只能在a,ba,b在點序列中相鄰的兩個點上取到,然後在轉過這條直線之後,(a,b)(a,b)關於直線的相對位置會發生變化,所以每次轉過之後交換a,ba,b就行了。 程式碼:

#include<bits/stdc++.h>
#define N 1005
using namespace std;
inline int read(){
	int ans=
0,w=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();} while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar(); return ans*w; } struct Pot{double x,y;}p[N]; struct Line{int a,b;double k;}l[N*N]; int n,tot=0,num[N],pred[N]; double ans=1e18; inline Pot operator
-(Pot a,Pot b){return (Pot){a.x-b.x,a.y-b.y};} inline double operator*(Pot a,Pot b){return a.x*b.y-a.y*b.x;} inline bool Cmp(Pot a,Pot b){return a.x==b.x?a.y<b.y:a.x<b.x;} inline bool cmp(Line a,Line b){return a.k<b.k;} inline double calc(Pot a,Line b){return fabs((p[b.a]-a)*(p[b.b]-a))*0.5;
} int main(){ n=read(); for(int i=1;i<=n;++i)p[i].x=read()*1.0,p[i].y=read()*1.0,pred[i]=num[i]=i; sort(p+1,p+n+1,Cmp); for(int i=1;i<n;++i)for(int j=i+1;j<=n;++j)l[++tot]=(Line){i,j,p[i].x==p[j].x?1e18:(double)((double)p[i].y-p[j].y)/((double)p[i].x-p[j].x)}; sort(l+1,l+tot+1,cmp); for(int i=1;i<=tot;++i){ int a=num[l[i].a],b=num[l[i].b]; if(a>b)swap(a,b); if(a^1)ans=min(ans,calc(p[pred[a-1]],l[i])); if(b^n)ans=min(ans,calc(p[pred[b+1]],l[i])); swap(num[l[i].a],num[l[i].b]),swap(pred[a],pred[b]); } printf("%.2lf",ans); return 0; }