poj 3525 Most Distant Point from the Sea 半平面交 + 二分

题目来源:

http://poj.org/problem?id=3525

 

分析:
题意:给定一个凸多边形,求多边形中距离边界最远的点到边界的距离。
思路 : 每次将凸多边形每条边往里平移d,判断是否存在核;二分d即可。

多边形边上的点(x , y)往里平移d 后的 坐标: s , e  为向量的 起点和终点, len 为起点和终点的距离, h 为平移的距离

x' = x + dx 

y' = y + dy

dx = ( s.y - e.y ) / len * h ,( 原理 是 利用 三角形的相似比 )

dy = ( e.x - s.x  ) / len * h ;

 

代码如下:

 

using namespace std ;
const double EPS = 1e-10 ;
const int Max_N = 105 ;
double add(double a, double b){
    return (fabs(a + b) < EPS * (fabs(a) + fabs(b))) ? 0 : (a + b) ;
}
struct Point{
    double x, y;
    Point(){}
    Point(double x, double y):x(x),y(y){}
    Point operator - (Point a){
        return Point(add(x , -a.x) , add(y , -a.y)) ;
    }
    Point operator + (Point a){
        return Point(add(x ,a.x) , add(y , a.y)) ;
    }
    double operator ^(Point a){
        return add(x * a.y , - y * a.x) ;
    }
    Point operator *(double d){
        return Point(x * d, y * d) ;
    }
    double dist(Point a){
        return sqrt( add((x - a.x) * (x - a.x) , (y - a.y)*(y - a.y) ) ) ;
    }
};
struct Line{
    Point st, ed;
    Line(){}
    Line(Point st, Point ed):st(st),ed(ed){}
    bool onRight(Point a){ // 点在线右边
        return ((ed - st)^(a - st)) < 0 ;
    }
    bool parallel(Line l){
        return ((ed - st)^(l.ed - l.st)) == 0 ;
    }
    Point CrossNode(Line l){
        double d1 = (l.ed - l.st)^(l.st - st) ;
        double d2 = (l.ed - l.st)^(ed - st) ;
        return st + (ed - st)*(d1 / d2) ;
    }
    double jijiao(){
        return atan2(ed.y - st.y , ed.x - st.x) ;
    }
};
bool operator < (Line l , Line r){
    double lp = l.jijiao() ;
    double rp = r.jijiao() ;
    if(fabs(lp - rp) > EPS)
        return lp < rp ;
    return ((l.st - r.st)^(r.ed - r.st)) < 0 ;
}
Line dequeue[Max_N] ;
int halfPanelCross(Line line[] , int ln){
    int i, tn ;
    sort(line , line + ln ) ;
    for(i = tn = 1; i < ln ; i++){
        if( fabs(line[i].jijiao() - line[i - 1].jijiao())  > EPS )
            line[tn ++] = line[i] ;
    }
    ln = tn ;
    int bot = 0 ,  top = 1 ;
    dequeue[0] = line[0] ;
    dequeue[1] = line[1] ;
    Point topcross ,botcross ;
    for(i = 2 ; i < ln  ; i++){
        if(dequeue[top].parallel(dequeue[top - 1])   ||
           dequeue[bot].parallel(dequeue[bot + 1]) )
           return 0 ;
        while(bot < top &&
              line[i].onRight(topcross = dequeue[top].CrossNode(dequeue[top - 1])))
              top -- ;
        while(bot < top &&
              line[i].onRight( botcross = dequeue[bot].CrossNode(dequeue[bot + 1])))
              bot ++ ;
        dequeue[++ top] = line[i] ;
    }
    while(bot < top &&
          dequeue[bot].onRight(topcross = dequeue[top].CrossNode(dequeue[top - 1])))
          top -- ;
    while(bot < top &&
          dequeue[top].onRight(botcross = dequeue[bot].CrossNode(dequeue[bot + 1])))
          bot ++ ;
    if(top <= bot + 1) return 0 ;
    return 1;
}
Line tmp[Max_N] ;
Line List[Max_N] ;
void ploygonChange(double h, int ln){
    double len , dx ,dy ;
    for(int i= 0 ; i < ln ; i++){
        len = List[i].ed.dist(List[i].st) ;
        dx = (List[i].st.y - List[i].ed.y) / len * h ;
        dy = (List[i].ed.x - List[i].st.x) / len * h ;
        tmp[i].st.x = List[i].st.x + dx ;
        tmp[i].st.y = List[i].st.y + dy ;
        tmp[i].ed.x = List[i].ed.x + dx;
        tmp[i].ed.y = List[i].ed.y + dy ;
    }
}
double  BinSearch(int ln){
    double l , r, mid ;
    l = 0.0 , r = 20000.0 ;
    while(l + EPS < r){
        mid = (l + r) * 0.5 ;
        ploygonChange(mid , ln ) ;
        if(halfPanelCross(tmp , ln))
            l = mid ;
        else r = mid ;
    }
    return l;
}
int main(){
    int n;
    Point p[Max_N] ;
    while(scanf("%d" , &n) && n){
        for(int i = 0 ; i < n ; i++)
            scanf("%lf%lf" , &p[i].x , &p[i].y) ;
        for(int i = 0 ; i < n ; i++)
            List[i] = Line(p[i] , p[(i+1) % n]) ;
        double tt = BinSearch(n) ;
        printf("%.6lf\n" ,tt) ;
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值