Bzoj1185最小矩阵覆盖[旋转卡壳+凸包+处理[-0]情况]

题目链接


题目大意:
就 是 给 你 若 干 个 点 用 一 个 最 小 的 矩 形 把 这 些 点 覆 盖 掉 就是给你若干个点用一个最小的矩形把这些点覆盖掉


解题思路:
1. 首 先 很 明 显 我 们 可 以 对 这 些 点 求 一 个 凸 包 , 那 么 答 案 的 矩 形 的 一 定 是 卡 在 凸 包 外 面 的 1.首先很明显我们可以对这些点求一个凸包,那么答案的矩形的一定是卡在凸包外面的 1.
2. 我 们 手 动 模 一 下 就 是 逐 渐 增 加 凸 包 上 面 的 点 数 , 去 看 一 下 最 小 的 矩 形 覆 盖 情 况 , 就 是 矩 形 的 一 条 边 有 两 个 点 在 上 面 就 是 和 凸 多 边 形 一 条 边 重 合 , 其 他 三 条 边 就 卡 在 其 他 三 个 点 上 面 。 2.我们手动模一下就是逐渐增加凸包上面的点数,去看一下最小的矩形覆盖情况,就是矩形的一条边有两个点在上面就是和凸多边形一条边重合,其他三条边就卡在其他三个点上面。 2.
3. 那 么 我 们 就 可 以 O ( n ) 枚 举 一 下 矩 形 卡 着 哪 条 边 , 我 们 通 过 用 发 现 u p 点 和 b o t t e m 围 成 的 三 角 形 是 一 个 单 峰 函 数 。 l e f t 和 r i g h t 点 的 在 b o t t o m 处 的 投 影 也 是 一 个 凹 函 数 3.那么我们就可以O(n)枚举一下矩形卡着哪条边,我们通过用发现up点和bottem围成的三角形是一个单峰函数。left和right点的在bottom处的投影也是一个凹函数 3.O(n)upbottemleftrightbottom


在这里插入图片描述


#include <iostream>
#include <cstdio>
#include <stack>
#include <sstream>
#include <vector>
#include <map>
#include <cstring>
#include <deque>
#include <cmath>
#include <iomanip>
#include <queue>
#include <algorithm>
#include <set>
#define mid ((l + r) >> 1) 
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define hash Hash
#define next Next
#define f first
#define s second
using namespace std;
const int maxn = 1e5 + 10;
const double eps = 1e-10;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
struct Point//点或向量
{
    double x, y;
    Point() {}
    Point(double x, double y) :x(x), y(y) {}
};
typedef Point Vector;
double minsqr = 1e12;
int n;
Point ploy[maxn], sta[maxn], ans[10];
int dcmp(double x)//精度三态函数(>0,<0,=0)
{
    if (fabs(x) < eps)return 0;
    else if (x > 0)return 1;
    return -1;
}
double Dot(Vector a, Vector b)//内积 
{
    return a.x*b.x + a.y*b.y;
}
Vector operator + (Vector a, Vector b)//向量加法
{
    return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (Vector a, Vector b)//向量减法
{
    return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, double p)//向量数乘
{
    return Vector(a.x*p, a.y*p);
}
Vector operator / (Vector a, double p)//向量数除
{
    return Vector(a.x / p, a.y / p);
}
double Distance(Point a, Point b)//两点间距离
{
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
bool operator == (const Point &a, const Point &b)//向量相等
{
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}

bool operator<(Point a,Point b)
{
    if(!dcmp(a.y-b.y)) return a.x<b.x;
    return a.y<b.y;
}

double Cross(Vector a, Vector b)//外积
{
    return a.x*b.y - a.y*b.x;
}

double DistanceToLine(Point A, Point M, Point N)//点A到直线MN的距离,Error:MN=0
{
    return fabs(Cross(A - M, A - N) / Distance(M, N));
}

double relation(Point A, Point B, Point C)
{
    return Dot(B - A, C - A) / Distance(A,B) / Distance(A,B);
}
//求C在AB所在直线的垂足P坐标
Point pedal(Point A, Point B, Point C)
{
    double r = relation(A,B,C);
    Point res;
    res.x = A.x + r * (B.x - A.x);
    res.y = A.y + r * (B.y - A.y);
    return res;
}


bool cmp(Point a,Point b)
{
    if(!dcmp(a.x-b.x)) return a.y<b.y;
    return a.x<b.x;
}
int top = 0;
void Graham() {
    sort(ploy,ploy+n,cmp);
    for(int i = 0; i < n; ++ i) {
        while(top >= 2 && dcmp(Cross(sta[top]-sta[top-1],ploy[i]-sta[top-1])) <= 0) top--;
        sta[++top] = ploy[i];
    }
    int tmp = top;
    for(int i = n - 2; i >= 0; -- i) {
        while(top >= tmp + 1 && dcmp(Cross(sta[top]-sta[top-1],ploy[i]-sta[top-1])) <= 0) top--;
        sta[++ top] = ploy[i];
    }
    top --;
}


void qiake() {
    int left = 1, right = 1, up = 1;
    for(int i = 1; i <= top; ++ i) {
        while(dcmp(Cross(sta[i]-sta[up+1],sta[i+1]-sta[up+1]) - Cross(sta[i]-sta[up],sta[i+1]-sta[up])) >= 0) up = up % top + 1;
        while(dcmp(Dot(sta[right+1] - sta[i],sta[i+1]-sta[i]) - Dot(sta[right]-sta[i],sta[i+1]-sta[i])) >= 0) right = right % top + 1;
        if(i == 1) left = right;
        while(dcmp(Dot(sta[i]-sta[i+1],sta[left+1]-sta[i+1]) - Dot(sta[i]-sta[i+1],sta[left]-sta[i+1])) >= 0) left = left % top + 1;
        double L = DistanceToLine(sta[up],sta[i],sta[i+1]);
        double Len = Distance(sta[i],sta[i+1]);//i 和 i + 1 点之间的长度
        double botten = Dot(sta[right]-sta[i],sta[i+1]-sta[i]) / Len + Dot(sta[i]-sta[i+1],sta[left]-sta[i+1]) / Len - Len;
        if(dcmp(minsqr - botten * L) > 0) {//逆时针存储矩形
            minsqr = botten * L;
            ans[0] = pedal(sta[i],sta[i+1],sta[right]);
            ans[1] = pedal(ans[0],sta[right],sta[up]);
            ans[2] = pedal(ans[1],sta[up],sta[left]);
            ans[3] = pedal(sta[i],sta[i+1],sta[left]);
        }
    }
}

int main()
{
    IOS;
    cin >> n;
    for(int i = 0; i < n; ++ i) {
        double x, y;
        cin >> x >> y;
        ploy[i] = (Point){x,y};
    }
    Graham();
    qiake();
    cout << fixed << setprecision(5) << minsqr << endl;
    int tmp = 0;
    for(int i = 0; i <= 3; i ++)
        if(ans[i]<ans[tmp]) tmp = i;

    for(int i = 0; i < 4; ++ i)
    cout << fixed << setprecision(5) << ans[(i + tmp) % 4].x + eps/*防止-0的出现*/ << " " << ans[(i + tmp) % 4].y + eps << endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值