Gym - 101635K - 凸包+(三分或叉積)
阿新 • • 發佈:2018-10-31
題目連結:https://vjudge.net/problem/Gym-101635K
解題思路:
尋找最小覆蓋矩形使得能把蛋糕上面所有的點都覆蓋,求出他的寬度,高度不限.
那麼首先求出n個點組成的凸包.列舉凸包上的所有邊,再找凸包上的一個離這條邊最遠的點,經過此點做邊的平行線。
那麼此兩條平行線無限延長肯定能覆蓋所有的點,寬度就是兩平行直線的距離.
取每條邊都這麼做然後最後取答案最小的那一條邊.
那麼怎麼找到邊對應的那個點呢?
1.我們按凸包逆時針點列舉,對應邊的那個最遠點,一定是個"凸值",即逆時針列舉距離值會先增後減,(特殊情況是一開始列舉第一個點就是極值),求凸值就可以用三分來求,因為特殊情況有可能不是凸函式,所以區間至少保留4個以上,3個時退出.
2.叉積法同樣是按逆時針列舉凸包上的點,假設現在考慮邊(i,i+1),做向量(i+1)->i,從(i+1)->(i+2),(i+2)->(i+3)。。。直到兩個叉積>=0
也就是(i+1)->i*j->(j+1)>=0 , 那麼j點就是最遠的,可以用叉積面積方式理解(底是邊(i,i+1),高就是點到直線距離),也可以很容易理解用j這個點做邊的平行線,所有點一定都會被夾在中間.
三分法:
#include<math.h> #include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; typedef long long ll; const int mx = 2e5 + 10; int n,top,_x,_y,q[mx*2],m; double len; struct node { ll x,y; }s[mx],tubeg[mx],w; ll judge(node p1,node p2,node p0)//面積公式判斷正負值 { ll ans = (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y); return ans; } bool cmp(node a,node b) { ll c = judge(w,b,a);//極角排序,同角度按距離從小到大排 if(!c) return pow(a.x-w.x,2)+pow(a.y-w.y,2) < pow(b.x-w.x,2)+pow(b.y-w.y,2); return c < 0; } void Graham() { for(int i=0;i<n;i++) { while(top>1&&judge(tubeg[top-2],s[i],tubeg[top-1])>=0) top--;//在向量左側的點都去掉 tubeg[top++] = s[i]; } } double dist(node a,node b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } double across(node c,node a,node b) { return fabs((c.y-a.y)*(b.x-a.x) - (c.x-a.x)*(b.y-a.y)); } double Get(int x,node a,node b) { node c = tubeg[x]; return across(c,a,b) / len; } double sanfen(int l,int r,node a,node b) { while(l<r-2){ int mid = (l+r)>>1; int mmid = mid+1; if(Get(q[mid],a,b)>Get(q[mmid],a,b)) r = mmid; else l = mid; } return max(Get(q[l],a,b),max(Get(q[r],a,b),Get(q[r-1],a,b))); } int main() { while(~scanf("%d%d",&n,&m)) { int a,b,p = 0; for(int i=0;i<n;i++){ scanf("%lld%lld",&s[i].x,&s[i].y); if(s[i].y<s[p].y) p = i; else if(s[i].y==s[p].y&&s[i].x<s[p].x) p = i; } swap(s[0],s[p]),w.x = s[0].x,w.y = s[0].y; sort(s+1,s+n,cmp);//以最下左那個座標為參考座標做極角排序 Graham(); double ans = 1e9; int l = 0, r = 0,eng = top-1; for(int i=2;i<top;i++) q[r++] = i; for(int i=0;i<top;i++){ len = dist(tubeg[i],tubeg[(i+1)%top]); ans = min(ans,sanfen(l,r-1,tubeg[i],tubeg[(i+1)%top])); eng = (eng+1)%top; l++,q[r++] = eng; } len = dist(tubeg[0],tubeg[1]); printf("%.10lf\n",ans); } return 0; }
叉積法:
#include<math.h> #include<iostream> #include<cstring> #include<algorithm> #include<cstdio> using namespace std; typedef long long ll; const int mx = 2e5 + 10; int n,top,_x,_y,m; double len; struct node { ll x,y; }s[mx],tubeg[mx],w; ll judge(node p1,node p2,node p0)//面積公式判斷正負值 { ll ans = (p1.x-p0.x)*(p2.y-p0.y) - (p2.x-p0.x)*(p1.y-p0.y); return ans; } ll judge2(node a,node b) { return a.x*b.y - b.x*a.y; } bool cmp(node a,node b) { ll c = judge(w,b,a);//極角排序,同角度按距離從小到大排 if(!c) return pow(a.x-w.x,2)+pow(a.y-w.y,2) < pow(b.x-w.x,2)+pow(b.y-w.y,2); return c < 0; } void Graham() { for(int i=0;i<n;i++) { while(top>1&&judge(tubeg[top-2],s[i],tubeg[top-1])>=0) top--;//在向量左側的點都去掉 tubeg[top++] = s[i]; } } double dist(node a,node b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } double Get(int x,node a,node b) { node c = tubeg[x]; return fabs( judge(b,c,a) / len ); } node cut(node a,node b) { return node{a.x-b.x,a.y-b.y}; } int main() { while(~scanf("%d%d",&n,&m)) { int a,b,p = 0; for(int i=0;i<n;i++){ scanf("%lld%lld",&s[i].x,&s[i].y); if(s[i].y<s[p].y) p = i; else if(s[i].y==s[p].y&&s[i].x<s[p].x) p = i; } swap(s[0],s[p]),w.x = s[0].x,w.y = s[0].y; sort(s+1,s+n,cmp); Graham(); double ans = 1e9; int head = 1; for(int i=0;i<top;i++){ len = dist(tubeg[i],tubeg[(i+1)%top]); node now = cut(tubeg[i],tubeg[(i+1)%top]); while(judge2(now,cut(tubeg[(head+1)%top],tubeg[head]))<0) head = (head+1)%top; ans = min(ans,Get(head,tubeg[i],tubeg[(i+1)%top])); } printf("%.10lf\n",ans); } return 0; }