[Template] Minimum rectangle coverage

Front cheese: convex hull, rotating card shell, basic operations of vectors

Title

Given a bunch of points on the plane, find a rectangle with the smallest area that can cover all points, and output the area and the coordinates of the four vertices.

analysis

A good computational geometry cancer problem. (nonsense)

After some paintings, we can draw a conclusion very emotionally: Finally, this rectangle must have one side coincident with one side on the convex hull of these points.

In other words, the final rectangle should grow like the following.

VlElb8.png

Then we obviously need to find the convex hull first, and then enumerate an edge on the convex hull to overlap the bottom edge of the rectangle.

Then observe this picture, we can find that three more points are needed to determine this rectangle: that is, the top point EEE (ie the opposite heel point of the bottom side), the leftmost and rightmost pointsF, CF, CF,C . As long as you find these three points, you can determine this rectangle.

One of the easiest points should be the top point, which is actually the template of the rotating card. Use the cross product to judge the distance from the point to the straight line.

Then there are two points on the left and right. These two points are roughly the same as the rotating jam. They also maintain a monotonous queue. The points on the convex hull are traversed counterclockwise. The only difference is the judgment condition.

Observe the above figure carefully. When traversing counterclockwise, before traversing to the rightmost point, all edges and bottom edges on the convex hull (such as AB ⃗ \vec{AB}A B And BC ⃗ \vec{BC}BC ) Is always positive; but after the rightmost point (eg CD ⃗ \vec{CD}CD ), the dot product becomes negative; then it becomes positive again until the leftmost point is passed.

So the problem is very simple, we apply the board of the rotating card and change the part judged by the cross product to the dot product judgment, and then we can find the leftmost point and the rightmost point.


After finding the three points, we consider how to find the area and vertex coordinates of the rectangle. Since the height of the rectangle is perpendicular to the bottom edge, we can find the vertical line of the bottom edge first. Then you can combine the left and right points to find the straight lines on the left and right sides of the rectangle, and then combine the heights determined by the upper and lower points to easily find the area and vertex of the rectangle. (Specifically, it can be understood with the code)

Code

First on the board sub- problem: P3187 minimum rectangle coverage

#include <bits/stdc++.h>
#define MAX 100005
#define eps 1e-8
using namespace std;

struct vec{
    
    
    double x, y;

    friend bool operator <(vec a, vec b){
    
    		//真·小于
        if(fabs(a.x-b.x) > eps) return a.x < b.x;
        return a.y < b.y;
    }
    friend bool operator <= (vec a, vec b){
    
    		//用来逆时针输出
        if(fabs(a.y-b.y) > eps) return a.y < b.y;
        return a.x < b.x;
    }

    friend vec operator +(vec a, vec b){
    
    
        return (vec){
    
    a.x+b.x, a.y+b.y};
    }
    friend vec operator -(vec a, vec b){
    
    
        return (vec){
    
    a.x-b.x, a.y-b.y};
    }
    friend vec operator ^(vec a, double b){
    
    		//数乘
        return (vec){
    
    a.x*b, a.y*b};
    }
    friend double operator %(vec a, vec b){
    
    		//点积
        return a.x*b.x+a.y*b.y;
    }
    friend double operator *(vec a, vec b){
    
    		//叉积
        return a.x*b.y-a.y*b.x;
    }
}a[MAX], h[MAX];

double dis(vec a, vec b){
    
               //两点之间的距离
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

double dis(vec a, vec b, vec c){
    
        //c到直线ab的距离
    return fabs((c-a)*(c-b)/dis(a, b));
}

vec inter(vec a, vec b, vec c, vec d){
    
           //直线ab和cd的交点
    double s = (c-a)*(d-a);
    double S = s+(d-b)*(c-b);
    return a+((b-a)^(s/S));
}

int n;
int st[MAX], top, vis[MAX];
vec pt[5];

int main()
{
    
    
    cin >> n;
    for(int i = 1; i <= n; ++i){
    
    
        scanf("%lf%lf", &a[i].x, &a[i].y);
    }
    sort(a+1, a+n+1);
    st[++top] = 1;
    for(int i = 2; i <= n; ++i){
    
    		//求凸包
        while(top > 1 && (a[st[top]]-a[st[top-1]]) * (a[i]-a[st[top]]) <= 0){
    
    
            vis[st[top]] = 0;
            top--;
        }
        vis[i] = 1;
        st[++top] = i;
    }
    int tmp = top;
    for(int i = n-1; i >= 1; --i){
    
    		
        if(vis[i]) continue;
        while(top > tmp && (a[st[top]]-a[st[top-1]]) * (a[i]-a[st[top]]) <= 0){
    
    
            vis[st[top]] = 0;
            top--;
        }
        vis[i] = 1;
        st[++top] = i;
    }
    for(int i = 1; i <= top; ++i){
    
    
        h[i] = a[st[i]];
    }
    top--;
    int l, r = 1, t = 1;
    while(dis(h[1], h[2], h[t]) <= dis(h[1], h[2], h[t+1])){
    
    
        t = t%top+1;
    }
    l = t; //确定最左点的初始位置,因为最左点必须在最右点的后面,否则初始值为1就会找到错误的点积大于0的点
    double ans = 1e18;
    for(int i = 1; i <= top; ++i){
    
    
        //找出三个关键点,套用旋转卡壳板子
        while((h[r+1]-h[r]) % (h[i+1]-h[i]) >= 0)
            r = r%top+1;
        while(dis(h[i], h[i+1], h[t]) <= dis(h[i], h[i+1], h[t+1]))
            t = t%top+1;
        while((h[l+1]-h[l]) % (h[i+1]-h[i]) <= 0)
            l = l%top+1;
        vec cx = (vec){
    
    -(h[i+1]-h[i]).y, (h[i+1]-h[i]).x};      //垂线
        double area = dis(h[i], h[i+1], h[t])*dis(h[r], h[r]+cx, h[l]);	//求面积
        if(area < ans){
    
    
            ans = area;
            //利用直线间的交点求出顶点坐标
            pt[1] = inter(h[i], h[i+1], h[r], h[r]+cx);
            pt[2] = inter(h[r], h[r]+cx, h[t], h[t]+(h[i+1]-h[i]));
            pt[3] = inter(h[t], h[t]+(h[i+1]-h[i]), h[l], h[l]+cx);
            pt[4] = inter(h[l], h[l]+cx, h[i], h[i+1]);
        }
    }
    for(int i = 1; i <= 4; ++i){
    
    		//防止出现-0.00000
        if(fabs(pt[i].x) < eps) pt[i].x = 0.0;
        if(fabs(pt[i].y) < eps) pt[i].y = 0.0;
    }
    int mn = 1;
    for(int i = 2; i <= 4; ++i){
    
    			//保证逆时针输出
        if(pt[i] <= pt[mn]) mn = i;
    }
    printf("%.5lf\n", ans);
    for(int i = mn; i <= 4; ++i)
        printf("%.5lf %.5lf\n", pt[i].x, pt[i].y);
    for(int i = 1; i < mn; ++i)
        printf("%.5lf %.5lf\n", pt[i].x, pt[i].y);

    return 0;
}

Guess you like

Origin blog.csdn.net/qq_30115697/article/details/90717223