HDU 6617 Enveloping Convex(凸包+半平面交+二分)
首先对于这m个点维护出一个凸包M,那么问题就变成了判断凸包P进行放大缩小能不能包含凸包M。(凸包P可以进行中心对称变换再进行放大缩小,见题意)
如何判断合适的相似比呢,我们可以用二分去放大缩小凸包P的坐标,得到最小的相似比。
接下来就是如何判断是否包含。我们需要对凸包P上的每一条向量,在凸包M上找到这么一个点,使得这个点左侧的所有凸包M上的点都在向量的左侧,那么我们可以直接同时逆时针枚举,用一个变量维护凸包M上的点,因为是同逆时针,凸包M上的点至少有一个只会被遍历一次,那么复杂度可以证明为On,这样就可以维护出来凸包P上的向量所对应的在凸包M上的点。因为包含关系,满足所有的向量的对应点都应该在向量的左侧,那么我们将这个向量相对于这个点的坐标求出来,然后维护一个半平面交是否有解即可,判断这个半平面交维护出来的是否是一个合法的凸包,当然由于我们是逆时针枚举的向量,需要先对凸包P进行逆时针转动,所以我们没必要将这些相对于坐标的向量排序,直接维护半平面交,但是最后我们需要判断维护出来的凸包是否严格按照逆时针旋转,因为位置是相对的,可能出现一个向下的向量的左侧是一个向上的向量,这样的关系是矛盾的。
注意细节,由于我的模板用的是求直线交点,精度比较差,eps开的比较小。
1 // ——By DD_BOND 2 3 //#include<bits/stdc++.h> 4 //#include<unordered_map> 5 //#include<unordered_set> 6 #include<functional> 7 #include<algorithm> 8 #include<iostream> 9 //#include<ext/rope> 10 #include<iomanip> 11 #include<climits> 12 #include<cstring> 13 #include<cstdlib> 14 #include<cstddef> 15 #include<cstdio> 16 #include<memory> 17 #include<vector> 18 #include<cctype> 19 #include<string> 20 #include<cmath> 21 #include<queue> 22 #include<deque> 23 #include<ctime> 24 #include<stack> 25 #include<map> 26 #include<set> 27 28 #define fi first 29 #define se second 30 #define MP make_pair 31 #define pb push_back 32 33 #pragma GCC optimize(3) 34 #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native") 35 36 typedef long long ll; 37 38 using namespace std; 39 40 const int MAXN=1e5+10; 41 const double eps=1e-18; 42 const double pi=acos(-1.0); 43 const ll INF=0x3f3f3f3f3f3f3f3f; 44 45 inline int dcmp(double x){ 46 if(fabs(x)<eps) return 0; 47 return (x>0? 1: -1); 48 } 49 50 inline double sqr(double x){ return x*x; } 51 52 struct Point{ 53 double x,y; 54 Point(){ x=0,y=0; } 55 Point(double _x,double _y):x(_x),y(_y){} 56 void input(){ scanf("%lf%lf",&x,&y); } 57 void output(){ printf("%.2f %.2f\n",x,y); } 58 bool operator <(const Point &b)const{ 59 return (dcmp(x-b.x)==0? dcmp(y-b.y)<0 : x<b.x); 60 } 61 double operator ^(const Point &b)const{ //叉积 62 return x*b.y-y*b.x; 63 } 64 double operator *(const Point &b)const{ //点积 65 return x*b.x+y*b.y; 66 } 67 bool operator ==(const Point &b)const{ 68 return dcmp(x-b.x)==0&&dcmp(y-b.y)==0; 69 } 70 Point operator +(const Point &b)const{ 71 return Point(x+b.x,y+b.y); 72 } 73 Point operator -(const Point &b)const{ 74 return Point(x-b.x,y-b.y); 75 } 76 Point operator *(double a){ 77 return Point(x*a,y*a); 78 } 79 Point operator /(double a){ 80 return Point(x/a,y/a); 81 } 82 double len2(){ //长度平方 83 return sqr(x)+sqr(y); 84 } 85 double len(){ //长度 86 return sqrt(len2()); 87 } 88 }; 89 90 inline double cross(Point a,Point b){ //叉积 91 return a.x*b.y-a.y*b.x; 92 } 93 94 inline double dot(Point a,Point b){ //点积 95 return a.x*b.x+a.y*b.y; 96 } 97 98 struct Line{ 99 Point s,e; 100 Line(){} 101 Line(Point _s,Point _e):s(_s),e(_e){} 102 Point operator &(const Line &b)const{ //求两直线交点 103 Point res=s; 104 double t=((s-b.s)^(b.s-b.e))/(((s-e)^(b.s-b.e))+eps); 105 res.x+=(e.x-s.x)*t; 106 res.y+=(e.y-s.y)*t; 107 return res; 108 } 109 }; 110 111 int relation(Point p,Line l){ //点和向量关系 1:左侧 2:右侧 3:在线上 112 int c=dcmp(cross(p-l.s,l.e-l.s)); 113 if(c<0) return 1; 114 else if(c>0) return 2; 115 else return 3; 116 } 117 118 bool counter_wise(Point *p,int n){ //多边形点集调整为逆时针顺序 119 for(int i=1;i<n-1;i++) 120 if(dcmp(cross(p[i]-p[i-1],p[i+1]-p[i-1]))>0) return 0; 121 else if(dcmp(cross(p[i]-p[i-1],p[i+1]-p[i-1]))<0){ 122 reverse(p,p+n); 123 return 1; 124 } 125 return 1; 126 } 127 128 Point tmp[MAXN]; 129 int convex_hull(Point *p,int n,Point *ch){ //求凸包 130 int m=0; 131 sort(p,p+n); 132 for(int i=0;i<n;i++){ 133 while(m>1&&dcmp(cross(tmp[m-1]-tmp[m-2],p[i]-tmp[m-1]))<=0) m--; 134 tmp[m++]=p[i]; 135 } 136 int k=m; 137 for(int i=n-2;i>=0;i--){ 138 while(m>k&&dcmp(cross(tmp[m-1]-tmp[m-2],p[i]-tmp[m-1]))<=0) m--; 139 tmp[m++]=p[i]; 140 } 141 if(n>1) m--; 142 for(int i=0;i<m;i++) ch[i]=tmp[i]; 143 return m; 144 } 145 146 Line que[MAXN]; 147 int half_plane_intersection(Line *L,int n){ //以逆时针方向 半平面交求多边形的核 ch表示凸包的顶点 返回顶点数 -1则表示不存在 148 int head=0,tail=1; 149 que[0]=L[0],que[1]=L[1]; 150 for(int i=2;i<n;i++){ 151 while(tail>head&&relation(que[tail]&que[tail-1],L[i])==2) tail--; 152 while(tail>head&&relation(que[head]&que[head+1],L[i])==2) head++; 153 que[++tail]=L[i]; 154 } 155 while(tail>head&&relation(que[tail]&que[tail-1],que[head])==2) tail--; 156 while(tail>head&&relation(que[head]&que[head+1],que[tail])==2) head++; 157 for(int i=head;i<=tail;i++){ 158 int j=(i==tail? head: i+1); 159 if(dcmp(cross(que[i].e-que[i].s,que[j].e-que[j].s))<=0) 160 return 0; 161 } 162 return 1; 163 } 164 165 Line line[MAXN]; 166 Point p[MAXN],q[MAXN],ops[MAXN]; 167 168 int main(void){ 169 int T; scanf("%d",&T); 170 while(T--){ 171 int n; scanf("%d",&n); 172 for(int i=0;i<n;i++) p[i].input(); 173 int m; scanf("%d",&m); 174 for(int i=0;i<m;i++) q[i].input(); 175 counter_wise(p,n); m=convex_hull(q,m,q); 176 double ans=INF; 177 for(int t=1;t<=2;t++){ 178 for(int i=0;i<n;i++) p[i]=Point(-p[i].x,-p[i].y); 179 for(int i=0,j=0;i<n;i++){ 180 while(dcmp(cross(p[(i+1)%n]-p[i],q[(j-1+m)%m]-q[j]))<0||dcmp(cross(p[(i+1)%n]-p[i],q[(j+1)%m]-q[j]))<0) j=(j+1)%m; 181 ops[i]=q[j]; 182 } 183 double l=0,r=1e10; 184 for(int i=0;i<70;i++){ 185 double mid=(l+r)/2; 186 for(int j=0;j<n;j++) line[j]=Line(p[j]*mid-ops[j],p[(j+1)%n]*mid-ops[j]); 187 if(half_plane_intersection(line,n)) r=mid; 188 else l=mid; 189 } 190 ans=min(ans,l); 191 } 192 printf("%.10lf\n",ans); 193 } 194 return 0; 195 }