[Plantilla] Cobertura mínima del rectángulo

Queso frontal: casco convexo, carcasa de tarjeta giratoria, operaciones básicas de vectores

Título

Dado un montón de puntos en el plano, encuentre un rectángulo con el área más pequeña que pueda cubrir todos los puntos y genere el área y las coordenadas de los cuatro vértices.

análisis

Un buen problema de cáncer de geometría computacional . (disparates)

Después de pintar un poco, podemos sacar una conclusión muy emotiva: Finalmente, este rectángulo debe tener un lado coincidente con un lado del casco convexo de estos puntos.

En otras palabras, el rectángulo final debería crecer como el siguiente.

VlElb8.png

Entonces, obviamente, primero necesitamos encontrar el casco convexo y luego enumerar un borde en el casco convexo para superponer el borde inferior del rectángulo.

Luego observe esta imagen, podemos encontrar que se necesitan tres puntos más para determinar este rectángulo: es decir, el punto superior EEE (es decir, el punto opuesto del talón del lado inferior), los puntos más a la izquierda y más a la derechaF, CF, CF ,C . Siempre que encuentre estos tres puntos, puede determinar este rectángulo.

Uno de los puntos más fáciles debería ser el punto superior, que en realidad es la plantilla de la tarjeta giratoria. Utilice el producto cruzado para juzgar la distancia desde el punto hasta la línea recta.

Luego hay dos puntos a la izquierda y a la derecha. Estos dos puntos son aproximadamente los mismos que el atasco giratorio. También mantienen una cola monótona. Los puntos del casco convexo se atraviesan en sentido antihorario. La única diferencia es la condición de juicio.

Observe atentamente la imagen de arriba. Al atravesar en sentido antihorario, antes de atravesar al punto más a la derecha, todos los bordes y bordes inferiores del casco convexo (como AB ⃗ \ vec {AB}A B Y BC ⃗ \ vec {BC}B C ) Siempre es positivo; pero después del punto más a la derecha (como CD ⃗ \ vec {CD}C D ), el producto escalar se vuelve negativo y luego vuelve a ser positivo hasta que se pasa el punto más a la izquierda.

Entonces, el problema es muy simple: aplicamos el tablero de la tarjeta giratoria y cambiamos la parte juzgada por el producto cruzado por el criterio del producto escalar, y luego podemos encontrar el punto más a la izquierda y el punto más a la derecha.


Después de encontrar los tres puntos, consideramos cómo encontrar el área y las coordenadas del vértice del rectángulo. Dado que la altura del rectángulo es perpendicular al borde inferior, primero podemos encontrar la línea vertical del borde inferior. Luego, puede combinar los puntos izquierdo y derecho para encontrar las líneas rectas en los lados izquierdo y derecho del rectángulo, y luego combinar las alturas determinadas por los puntos superior e inferior para encontrar fácilmente el área y el vértice del rectángulo. (Específicamente se puede entender junto con el código)

Código

Primero en el subproblema de la placa: cobertura mínima del rectángulo P3187

#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;
}

Supongo que te gusta

Origin blog.csdn.net/qq_30115697/article/details/90717223
Recomendado
Clasificación