zoukankan      html  css  js  c++  java
  • HDU4454 Stealing a Cake 三分枚举

    题意:给定一个点,一个圆,以及一个矩形,现在问从一个点到一个圆再到一个矩形的最短距离为多少?到达一个目标可以只挨着或者穿过它。

    解法:目前只知道从一个点到圆上按照[0,PI],[PI,2*PI]的两个半圆进行划分确实是满足距离是一个凹函数,但是加上到矩形的距离后仍然满足三分性质则不知道怎么得到的。具体做法是先将整个坐标系平移使得圆心落在(0, 0)处,然后三分枚举圆上的点,距离由两部分组成,一部分是点到圆上点的距离,一部分是点到矩形的距离,后者转化为点到四条线段的距离求解。

    代码如下:

    #include <cstdlib>
    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <iostream>
    #include <cmath>
    using namespace std;
    
    const double eps = 1e-6;
    const double PI = acos(-1.0);
    double cx, cy, cr; // 圆的圆心以及半径
    
    inline int sign(double x) {
        return x < -eps ? -1 : x > eps ? 1 : 0;
    }
    
    struct Point {
        double x, y;
        Point (double xx = 0, double yy = 0) : x(xx), y(yy) {}
        Point operator - (const Point &ot) const { // 两个点作差获得向量 
            return Point(x - ot.x , y - ot.y);
        }
        double operator * (const Point &ot) const { // 重载点积
            return x * ot.x + y * ot.y;
        }
        double operator ^ (const Point &ot) const { // 重载叉积 
            return x * ot.y - y * ot.x;
        }
        bool operator < (const Point &ot) const {
            if (sign(x - ot.x)) {
                return sign(x - ot.x) < 0; // 首先按照横坐标排序 
            } else { // 然后按照纵坐标排序 
                return sign(y - ot.y) < 0;
            }
        }
        void show() {
            printf("x = %.2f, y = %.2f\n", x, y);
        }
    };
    
    Point it, rp[4];
    
    double dist(const Point &a, const Point &b) {
        return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
    }
    
    double dtoL(const Point &np, const Point &st, const Point &ed) { // 点到线段的距离 
        double ret; 
        if (sign((ed-st)*(np-st)) > 0 && sign((st-ed)*(np-ed) > 0)) { // 用来判定一个该点的投影在该线段上
            ret = fabs((st-np)^(ed-np)) / dist(st, ed);
        } else {
            ret = min(dist(np, st), dist(np, ed)); // 点到两个端点的较小值
        }
        return ret;
    }
    
    double dtoR(const Point &np) { // 到四条线段的最短距离 
        double d1 = min(dtoL(np, rp[0], rp[1]), dtoL(np, rp[0], rp[2]));
        double d2 = min(dtoL(np, rp[1], rp[3]), dtoL(np, rp[2], rp[3]));
        return min(d1, d2);
    }
    
    double tsearch(double l, double r) {
        double delta;
        while (r - l >= eps) {
            delta = (r - l) / 3.0; // delta表示是区间长度的1/3
            Point Lp(cr*cos(l+delta), cr*sin(l+delta));
            Point Rp(cr*cos(r-delta), cr*sin(r-delta));
            double d1 = dist(it, Lp) + dtoR(Lp);
            double d2 = dist(it, Rp) + dtoR(Rp);
            if (sign(d1 - d2) > 0) {
                l += delta;
            } else {
                r -= delta;
            }
        }
        Point fp(cr*cos(r), cr*sin(r));
        return dist(it, fp) + dtoR(fp);
    }
    
    void solve() {
        // 先三分[0, PI],在三分[PI, 2*PI]内的角度
        printf("%.2f\n", min(tsearch(0, PI), tsearch(PI, 2*PI)));
    }
    
    int main() {
        while (scanf("%lf %lf", &it.x, &it.y), sign(it.x) | sign(it.y)) {
            scanf("%lf %lf %lf", &cx, &cy, &cr); // 读取圆心坐标和半径
            it.x -= cx, it.y -= cy;              // 将圆心平移到源点,其余坐标均作出改变
            scanf("%lf %lf", &rp[0].x, &rp[0].y);
            rp[0].x -= cx, rp[0].y -= cy;
            scanf("%lf %lf", &rp[1].x, &rp[1].y);
            rp[1].x -= cx, rp[1].y -= cy;
            rp[2].x = rp[0].x, rp[2].y = rp[1].y;
            rp[3].x = rp[1].x, rp[3].y = rp[0].y;
            sort(rp, rp+4); // 获得矩形的四个顶点,并排序,方便得到线段与点的关系
            double t = sqrt(2.0) / 2;
            solve();
        }
        return 0;
    }
  • 相关阅读:
    利用Python中SocketServer 实现客户端与服务器间非阻塞通信
    Redis单机配置多实例,实现主从同步
    Django(一) 安装使用基础
    数组模拟环形队列
    单链表原理及应用举例
    中缀表达式转换为后缀表达式(思路)
    中缀表达式转后缀表达式(代码实现)及前缀表达式思路补充
    数组模拟栈(Stack)
    双向链表原理及应用举例
    单向环形链表
  • 原文地址:https://www.cnblogs.com/Lyush/p/3108485.html
Copyright © 2011-2022 走看看