1. 程式人生 > 其它 >UVA10173 Smallest Bounding Rectangle 題解

UVA10173 Smallest Bounding Rectangle 題解

題意

給定多組資料,每組資料給定\(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;
}

感謝觀看!!!