【题解】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;
}

感谢观看!!!

posted @ 2022-06-01 10:06  Jekyll_Y  阅读(36)  评论(0编辑  收藏  举报