2018.10.03 bzoj3707: 圈地(計算幾何)
阿新 • • 發佈:2018-12-13
傳送門 計算幾何好題。
本蒟蒻表示不看題解只會。 正解是先考慮把直線按照斜率從小到大排序,然後把點按座標排序。 這樣每次列舉到直線時,離直線最近的點只能在在點序列中相鄰的兩個點上取到,然後在轉過這條直線之後,關於直線的相對位置會發生變化,所以每次轉過之後交換就行了。 程式碼:
#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;
}