UVA10173 Smallest Bounding Rectangle 題解
阿新 • • 發佈:2022-06-01
題意
給定多組資料,每組資料給定\(n\)個座標,求能覆蓋這\(n\)個點的最小矩形,並輸出其面積。
思路
其實這道題跟P3187 [HNOI2007]最小矩形覆蓋並沒有多大的區別,而且也省去了求矩形頂點座標的步驟,(當時內心是非常開心,但發現這資料比P3187強得多的時候,內心是崩潰的。。。),但這道題顯然資料是強得太多了,我一份A了P3187的程式碼並沒有過不了這道題。。(我後來改了一下找左邊點時的旋轉方向好像就A了)想要比較強的資料的話可以上這裡。
對於最小矩形覆蓋,其最小矩形的一條邊必定在這\(n\)個點的凸包上,這樣我們就先求出凸包再使用旋轉卡殼,找到在以當前邊為矩形的一條邊時,在最左邊,最右邊,最上邊的點,然後用向量加減的方法求出矩形的高和寬,再進一步求面積就好了。
(別忘了會出現最小矩形覆蓋無解的情況)。
Code
#include<bits/stdc++.h> #define double long double #define eps 1e-18 using namespace std; const int maxn=5e4+10; struct geometric { double x, y; geometric(double X=0, double Y=0) :x(X), y(Y) {} friend geometric operator + (const geometric a, const geometric b) { return geometric(a.x+b.x, a.y+b.y); } friend geometric operator - (const geometric a, const geometric b) { return geometric(a.x-b.x, a.y-b.y); } friend geometric operator * (const geometric a, double p) { return geometric(a.x*p, a.y*p); } friend geometric operator / (const geometric a, double p) { return geometric(a.x/p, a.y/p); }// 向量的四則運算 }p[maxn], st[maxn]; int n, cnt, top;double S=1e20; double dis(geometric a, geometric b) { return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)); } // 向量模長 double dot(geometric a1, geometric a2, geometric b1, geometric b2) { return (a2.x-a1.x)*(b2.x-b1.x)+(a2.y-a1.y)*(b2.y-b1.y); }// 點積 double cross(geometric a1, geometric a2, geometric b1, geometric b2) { return (a2.x-a1.x)*(b2.y-b1.y)-(a2.y-a1.y)*(b2.x-b1.x); } // 叉積 bool cmp(geometric a, geometric b) { double tmp; tmp=cross(p[1], a, p[1], b); if(tmp>0)return true; if(tmp==0)return dis(p[1], a)<=dis(p[1], b); return false; } int main() { while(true) { memset(st,0,sizeof(st)); memset(p,0,sizeof(p)); scanf("%d", &n); if(n==0)break; top=0;cnt=0;S=1e20; for(int i=1;i<=n;i++) { double x, y; scanf("%Lf%Lf", &x, &y); p[++cnt]=geometric(x, y); if(i==1) continue; if(p[cnt].y<p[1].y) swap(p[cnt], p[1]); if(p[cnt].y==p[1].y&&p[cnt].x>p[1].x) swap(p[cnt], p[1]); } sort(p+2, p+cnt+1, cmp); st[++top]=p[1]; for(int i=2;i<=cnt;i++) { while(top>1&&cross(st[top-1], st[top], st[top], p[i])<=0) top--; st[++top]=p[i]; } st[++top]=p[1]; // 求凸包 if(top<=3) { printf("0.0000\n"); continue; } // 凸包頂點只有兩個時,最小矩形覆蓋無解。 st[0]=st[top-1]; for(int i=2, j=3,l,r;i<=top;i++) { r=(i==top)?1:i;l=i-1; while(cross(st[i-1], st[i], st[i], st[j])<cross(st[i-1], st[i], st[i], st[j+1])) j=(j==top-1)?1:j+1; // 找最上邊的點 while(dot(st[i-1], st[i], st[i], st[r])<dot(st[i-1], st[i], st[i], st[r+1])) r=(r==top-1)?1:r+1; // 找最右邊的點 while(dot(st[i], st[i-1], st[i-1], st[l])<dot(st[i], st[i-1], st[i-1], st[l-1])) l=(l==1)?top-1:l-1; // 找最左邊的點 double wide, high, len=dis(st[i-1], st[i]); wide=fabs(dot(st[i], st[i-1], st[i-1], st[l]))/len+fabs(dot(st[i-1], st[i], st[i], st[r]))/len+len; // 寬 high=fabs(cross(st[i-1], st[i], st[i-1], st[j]))/len; // 高 S=min(S, high*wide); } printf("%.4Lf\n", S); } return 0; }
感謝觀看!!!