Link:
Solution:
很不错的一道思维题
可以发现枚举完两点后最优点就是最接近该直线的点
如果将该直线看作$y$轴用两边$x$坐标的绝对值最小的点更新即可
于是可以将所有斜率排序后不断旋转$y$轴并维护当前按$x$从小到大的序列
发现对于直线$(x,y)$,两点$x$坐标的相对关系仅在$y$轴斜率超过直线斜率后才会变化
因此每处理完一条直线,将两点的顺序交换即可!
注意:这题BZOJ好像有锅,本身就垂直于$y$轴的直线必须最后处理
Code:
#include <bits/stdc++.h> using namespace std; #define X first #define Y second #define pb push_back typedef double db; typedef pair<db,db> P; typedef long long ll; const int MAXN=1e3+10; db res=1e60,INF=1e60;P dat[MAXN]; int n,seq[MAXN],mp[MAXN],tot; P operator - (P a,P b) {return P(a.X-b.X,a.Y-b.Y);} inline db Cross(P a,P b) {return a.X*b.Y-a.Y*b.X;} inline db Slope(P a,P b) {return a.X!=b.X?(a.Y-b.Y)/(a.X-b.X):INF;} struct Line{double x,y,slope;}line[MAXN*MAXN]; bool cmp(Line a,Line b){return a.slope<b.slope;} inline db solve(int a,int b,int c) {return fabs(Cross(dat[b]-dat[a],dat[c]-dat[a]))/2.0;} int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lf%lf",&dat[i].X,&dat[i].Y); sort(dat+1,dat+n+1); for(int i=1;i<=n;i++) seq[i]=mp[i]=i; for(int i=1;i<n;i++) for(int j=i+1;j<=n;j++) //注意与y轴平行的线要放在最后处理(数据的锅?) line[++tot]=(Line){i,j,Slope(dat[i],dat[j])}; sort(line+1,line+tot+1,cmp); //将y轴不断旋转并维护当前从左向右的序列 for(int i=1;i<=tot;i++) { int a=line[i].x,b=line[i].y; if(seq[a]>seq[b]) swap(a,b); if(seq[a]>1) res=min(res,solve(a,b,mp[seq[a]-1])); if(seq[b]<n) res=min(res,solve(a,b,mp[seq[b]+1])); swap(seq[a],seq[b]); swap(mp[seq[a]],mp[seq[b]]); } printf("%.2lf",res); return 0; }