码迷,mamicode.com
首页 > 其他好文 > 详细

半平面交 模板 poj 3335 poj 3130 poj 1474 判断半平面交是否为空集

时间:2014-04-30 17:00:01      阅读:621      评论:0      收藏:0      [点我收藏+]

标签:com   http   blog   style   class   div   img   code   java   c   log   

半平面交模板

mamicode.com,码迷
const double pi= acos(-1.0);
#define arc(x) (x / 180 * pi)
const double EPS = 1e-8;
const int Max_N = 105;
struct Point{
    double x,y;
    Point(){}
    Point(double x, double y):x(x),y(y){}
    Point operator - (Point p){
        return Point(x- p.x , y - p.y ) ;
    }
    Point operator + (Point p){
        return Point(x+ p.x , y + p.y ) ;
    }
    double operator ^ ( Point p){
        return x*p.y - y * p.x;
    }
    double operator * (Point p){ //内积 点乘
        return x*p.x + y * p.y ;
    }
    Point operator * (double d){
        return Point( x*d, y*d ) ;
    }
    //点位置相同
    bool operator == (Point p){
        return fabs(x - p.x ) < EPS && fabs( y - p.y )< EPS ;
    }
};
// 两点表示的向量
struct pVector{
    Point s,e; // s 起点, e终点
    double a,b,c;  // 一般式  ax + by + c = 0
    pVector(){}
    pVector(Point s, Point e):s(s),e(e){}
    void output(){
        cout<<s.x <<" "<<s.y << ":" <<e.x <<" "<< e.y <<endl;
    }
    // 向量与点的叉乘[ 点相对向量位置判断 ]
    //返回值> 0 , 则 点在向量的右侧, <0 则 点在向量的左侧
    bool  operator^( Point p  ){
        return ( p - s) ^ (e- s);
    }
    bool onRight(Point p){ // 点在直线向量的右边
        return ( (p - s)^( e - s) )> EPS ;
    }
    // 向量与向量(_off)的 叉乘
    double operator ^ (pVector _off){
        return ((e - s )^( _off.e - _off.s )) ;
    }
    //从两点表示  转换为 一般表示
    bool pton(){
        a = s.y - e.y ;
        b = e.x - s.x ;
        c = s.x * e.y - s.y * e.x ;
        return true;
    }
    //两向量 (直线) 平行
    bool parallel(pVector _off){
        return fabs( (e - s)^( _off.e - _off.s ) ) < EPS ;
    }
    //两直线 交点, 参数 目标直线[_off]
    Point crossLPt(pVector _off){
        // 注意先判断平行和重合
        double d1 = ( (_off.e - _off.s)^( _off.s - s ) ) ;
        double d2 = ( ( _off.e - _off.s )^( e - s ) ) ;
        return s + ( e - s )*( d1 / d2 ) ;
    }
 };
 //------------半平面交  -------------//
 //半平面 计算 极角函数
 double hpc_pa(pVector _off){
    return atan2( _off.e.y - _off.s.y , _off.e.x - _off.s.x ) ;
}
// 半平面交 排序函数 [优先顺序:1极角  2. 前面的直线在后面的左边 ]
bool hpc_cmp(pVector l ,  pVector r){
    double lp = hpc_pa(l) , rp = hpc_pa(r);
    if( fabs(lp - rp) > EPS )
        return lp < rp ;
    return ( (l.s - r.s)^(r.e - r.s) ) < -EPS ;
}
//用于 计算的 双端队列
pVector dequeue[Max_N];
Point pt[Max_N] ; // 返回半平面交的 交点
int n;
//获取 半平面交的 多边形(多边形的核) o(n log(2n))
//参数: 向量集合[l], 向量数量[ln] (半平面方向在向量的左边)
//函数运行后 如果n[即返回多边形的点数量] 为 0 则不存在半平面交的多边形(不存在区域或区域面积无穷大)
int halfPanelCross(pVector _off[] ,  int ln){
    int i, tn ;
    sort( _off ,  _off + ln , hpc_cmp  ) ;
    // 平面在向量左边 的筛选
    for( i = tn = 1 ; i < ln ; i++ ){ // 处理极角相同的,选择向量方向最左边的
        if( fabs( hpc_pa( _off[i] ) - hpc_pa( _off[i-1] ) ) > EPS ) //除去极角相同的向量
            _off[tn ++] = _off[i] ;
    }
    ln = tn ; // 预处理后长度
    int bot = 0, top = 1 ;  // 队列的两个指针首尾
    dequeue[0] = _off[0] ;
    dequeue[1] = _off[1] ;
    for(i = 2 ; i < ln ; i++){  // 枚举有向直线
        if( dequeue[top].parallel( dequeue[top - 1] ) ||
            dequeue[bot].parallel( dequeue[bot + 1] )   )  // 剪枝 : 如果f方向相反的2个向量平行 则 为空集
                return 0;
        while( bot < top  &&   _off[i].onRight(  dequeue[top].crossLPt(dequeue[top - 1])   ) ) // 交点在向量的右边,删除头无用边 top--
            top-- ;
        while( bot< top  &&  _off[i].onRight(  dequeue[bot].crossLPt(dequeue[bot + 1])  ) )  //交点在向量的右边,删除尾无用边 bot ++
            bot ++ ;
        dequeue[ ++ top] = _off[i] ;  // 将当前直线加入队列
    }
    // 处理链接处 若队首交点在队尾直线的右边, 队首出列 , 若队尾的交点在队首直线的右边, 队尾出列
    while( bot < top && dequeue[bot].onRight( dequeue[top].crossLPt(dequeue[top - 1])  ))
        top -- ;
    while(bot < top && dequeue[top].onRight( dequeue[bot].crossLPt(dequeue[bot + 1]) ))
        bot ++ ;
    if( top <= bot + 1) return 0; // 若队列为空, 则空集
    n = 0;
    //计算交点(注意不同直线形成的交点可能重合)
    for(i = bot ; i < top ; i++){
        pt[n ++] = dequeue[i].crossLPt(dequeue[i + 1]) ;
    }
    if(bot < top + 1)
        pt[n ++] = dequeue[bot].crossLPt( dequeue[top] ) ;
    return n;
}
mamicode.com,码迷

 

 分析:  如果多边形顶点是按照 顺时针给出, 那么向量的构造为:

mamicode.com,码迷
for(i = 0; i<m ; i++){
            List[i] = pVector( p[(i+1) % m ], p[i] ) ;  //构造有向直线(半平面在向量的左边)
        }
mamicode.com,码迷

 

如果多变形顶点是按照 逆时针 给出, 那么向量的构造为:

mamicode.com,码迷
for(i = 0; i<m ; i++){
            List[i] = pVector(  p[i] , p[(i+1) % m ] ) ;  //构造有向直线(半平面在向量的左边)
        }
mamicode.com,码迷

 

题目来源:

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

判断是否存在半平面交

 

代码如下:

mamicode.com,码迷
const double pi= acos(-1.0);
#define arc(x) (x / 180 * pi)
const double EPS = 1e-8;
const int Max_N = 105;
struct Point{
    double x,y;
    Point(){}
    Point(double x, double y):x(x),y(y){}
    Point operator - (Point p){
        return Point(x- p.x , y - p.y ) ;
    }
    Point operator + (Point p){
        return Point(x+ p.x , y + p.y ) ;
    }
    double operator ^ ( Point p){
        return x*p.y - y * p.x;
    }
    double operator * (Point p){ //内积 点乘
        return x*p.x + y * p.y ;
    }
    Point operator * (double d){
        return Point( x*d, y*d ) ;
    }
    //点位置相同
    bool operator == (Point p){
        return fabs(x - p.x ) < EPS && fabs( y - p.y )< EPS ;
    }
};
// 两点表示的向量
struct pVector{
    Point s,e; // s 起点, e终点
    double a,b,c;  // 一般式  ax + by + c = 0
    pVector(){}
    pVector(Point s, Point e):s(s),e(e){}
    void output(){
        cout<<s.x <<" "<<s.y << ":" <<e.x <<" "<< e.y <<endl;
    }
    // 向量与点的叉乘[ 点相对向量位置判断 ]
    //返回值> 0 , 则 点在向量的右侧, <0 则 点在向量的左侧
    bool  operator^( Point p  ){
        return ( p - s) ^ (e- s);
    }
    bool onRight(Point p){ // 点在直线向量的右边
        return ( (p - s)^( e - s) )> EPS ;
    }
    // 向量与向量(_off)的 叉乘
    double operator ^ (pVector _off){
        return ((e - s )^( _off.e - _off.s )) ;
    }
    //从两点表示  转换为 一般表示
    bool pton(){
        a = s.y - e.y ;
        b = e.x - s.x ;
        c = s.x * e.y - s.y * e.x ;
        return true;
    }
    //两向量 (直线) 平行
    bool parallel(pVector _off){
        return fabs( (e - s)^( _off.e - _off.s ) ) < EPS ;
    }
    //两直线 交点, 参数 目标直线[_off]
    Point crossLPt(pVector _off){
        // 注意先判断平行和重合
        double d1 = ( (_off.e - _off.s)^( _off.s - s ) ) ;
        double d2 = ( ( _off.e - _off.s )^( e - s ) ) ;
        return s + ( e - s )*( d1 / d2 ) ;
    }
 };
 //------------半平面交  -------------//
 //半平面 计算 极角函数
 double hpc_pa(pVector _off){
    return atan2( _off.e.y - _off.s.y , _off.e.x - _off.s.x ) ;
}
// 半平面交 排序函数 [优先顺序:1极角  2. 前面的直线在后面的左边 ]
bool hpc_cmp(pVector l ,  pVector r){
    double lp = hpc_pa(l) , rp = hpc_pa(r);
    if( fabs(lp - rp) > EPS )
        return lp < rp ;
    return ( (l.s - r.s)^(r.e - r.s) ) < -EPS ;
}
//用于 计算的 双端队列
pVector dequeue[Max_N];
Point pt[Max_N] ; // 返回半平面交的 交点
int n;
//获取 半平面交的 多边形(多边形的核) o(n log(2n))
//参数: 向量集合[l], 向量数量[ln] (半平面方向在向量的左边)
//函数运行后 如果n[即返回多边形的点数量] 为 0 则不存在半平面交的多边形(不存在区域或区域面积无穷大)
int halfPanelCross(pVector _off[] ,  int ln){
    int i, tn ;
    sort( _off ,  _off + ln , hpc_cmp  ) ;
    // 平面在向量左边 的筛选
    for( i = tn = 1 ; i < ln ; i++ ){ // 处理极角相同的,选择向量方向最左边的
        if( fabs( hpc_pa( _off[i] ) - hpc_pa( _off[i-1] ) ) > EPS ) //除去极角相同的向量
            _off[tn ++] = _off[i] ;
    }
    ln = tn ; // 预处理后长度
    int bot = 0, top = 1 ;  // 队列的两个指针首尾
    dequeue[0] = _off[0] ;
    dequeue[1] = _off[1] ;
    for(i = 2 ; i < ln ; i++){  // 枚举有向直线
        if( dequeue[top].parallel( dequeue[top - 1] ) ||
            dequeue[bot].parallel( dequeue[bot + 1] )   )  // 剪枝 : 如果f方向相反的2个向量平行 则 为空集
                return 0;
        while( bot < top  &&   _off[i].onRight(  dequeue[top].crossLPt(dequeue[top - 1])   ) ) // 交点在向量的右边,删除头无用边 top--
            top-- ;
        while( bot< top  &&  _off[i].onRight(  dequeue[bot].crossLPt(dequeue[bot + 1])  ) )  //交点在向量的右边,删除尾无用边 bot ++
            bot ++ ;
        dequeue[ ++ top] = _off[i] ;  // 将当前直线加入队列
    }
    // 处理链接处 若队首交点在队尾直线的右边, 队首出列 , 若队尾的交点在队首直线的右边, 队尾出列
    while( bot < top && dequeue[bot].onRight( dequeue[top].crossLPt(dequeue[top - 1])  ))
        top -- ;
    while(bot < top && dequeue[top].onRight( dequeue[bot].crossLPt(dequeue[bot + 1]) ))
        bot ++ ;
    if( top <= bot + 1) return 0; // 若队列为空, 则空集
    n = 0;
    //计算交点(注意不同直线形成的交点可能重合)
    for(i = bot ; i < top ; i++){
        pt[n ++] = dequeue[i].crossLPt(dequeue[i + 1]) ;
    }
    if(bot < top + 1)
        pt[n ++] = dequeue[bot].crossLPt( dequeue[top] ) ;
    return n;
}
pVector List[Max_N];
Point p[Max_N];
int main(){
    int t, i , j;
    int m;
    scanf("%d" , &t);
    while(t--){
        scanf("%d",&m) ;
        for(i = 0 ; i< m ; i++)
            scanf("%lf%lf", &p[i].x , &p[i].y) ;
        for(i = 0; i<m ; i++){
            List[i] = pVector( p[(i+1) % m ], p[i] ) ;  //构造有向直线(半平面在向量的左边)
        }
        if ( halfPanelCross(List, m) )
            puts("YES");
        else puts("NO") ;
    }
    return 0;
}
mamicode.com,码迷

 

 

 

poj 3130 判段半平面交是否为空集,  多边形的顶点是按逆时针输入。 只需更改下 构造有向直线处的代码。

 

poj 1474  判断 半平面交 是否为空集, 多边形的顶点是按 顺时针输入, 只需更改下 输出代码, 即可。

半平面交 模板 poj 3335 poj 3130 poj 1474 判断半平面交是否为空集,码迷,mamicode.com

半平面交 模板 poj 3335 poj 3130 poj 1474 判断半平面交是否为空集

标签:com   http   blog   style   class   div   img   code   java   c   log   

原文地址:http://www.cnblogs.com/zn505119020/p/3699418.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!