判点是否在凸包内(二分极角序)

判点是否在凸包内(二分极角序)

解题思路:以p[1]为基点,将所有的点按极角序排好,然后共线都点都去掉(当然,这个处理还是有点小麻烦)。如果我们将所有的点跟p[1]相连,可以将凸包划分成n-2个扇形区。那么,我们可二分出要判断的点所在的扇形区,然后用差积判是在内侧还是外侧。

代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <math.h>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <stdlib.h>
#include <time.h>
#define lowbit(x) (x&(-x))
#define ll __int64
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
#define ls son[0][rt]
#define rs son[1][rt]
#define new_edge(a,b,c) edge[tot].t = b , edge[tot].v = c , edge[tot].next = head[a] , head[a] = tot ++
using namespace std;

struct Point {
    double x , y ;
    Point ( double _x = 0 , double _y = 0 ) : x(_x) , y(_y) {}
    void print () {
        printf ( "%.2lf,%.2lf\n" , x , y ) ;
    }
} ;

double cross ( Point o , Point a , Point b ) {
    Point u = Point ( a.x - o.x , a.y - o.y ) ;
    Point v = Point ( b.x - o.x , b.y - o.y ) ;
    return ( u.x * v.y - u.y * v.x ) ;
}

Point p[1111] ;

double length ( Point a , Point b ) {
    return (b.x-a.x) * (b.x-a.x) + (b.y-a.y) * (b.y-a.y) ;
}

bool cmp ( Point i , Point j ) {
    if ( cross ( p[1] , i , j ) == 0 ) return ( length ( p[1] , i ) < length ( p[1] , j )  ) ;
    return ( cross ( p[1] , i , j ) > 0 ) ;
}

int bin ( Point key , int r ) {
    int l = 2 ;
    while ( l <= r ) {
        int m = ( l + r ) >> 1 ;
        if ( cross ( p[1] , p[m] , key ) <= 0 ) r = m - 1 ;
        else l = m + 1 ;
    }
    return l ;
}

int vis[1111] ;
int main() {
    int n , i , j , k , l , m ;
   // freopen ( "a.txt" , "r" , stdin ) ;
    //freopen ( "b.txt" , "w" , stdout ) ;
    while ( scanf ( "%d" , &n ) != EOF ) {
        for ( i = 1 ; i <= n ; i ++ ) {
            getchar () ;
            scanf ( "(%lf,%lf)" , &p[i].x , &p[i].y ) ;
        }
        memset ( vis , 0 , sizeof ( vis ) ) ;
        sort ( p + 2 , p + n + 1 , cmp ) ;
        j = n - 1 ;
        while ( cross ( p[1] , p[j] , p[n] ) == 0 ) vis[j] = 1 , j -- ;
        int tot = 1 ;
        for ( i = 2 ; i <= n ; i ++ )
            if ( !vis[i] ) p[++tot] = p[i] ;
        n = tot ;
        tot = 2 ;
        for ( i = 3 ; i <= n ; i ++ ) {
            if ( cross ( p[tot-1] , p[tot] , p[i] ) == 0 ) p[tot] = p[i] ;
            else p[++tot] = p[i] ;
        }
        scanf ( "%d" , &m ) ;
        while ( m -- ) {
            double x , y ;
            getchar () ;
            scanf ( "(%lf,%lf)" , &x , &y ) ;
            Point u = Point (x,y) ;
            k = bin ( u , tot ) ;
            if ( k > tot ) puts ( "No" ) ;
            else if ( k == 2 ) {
                if ( cross (p[1] , p[2] , u) > 0 ) puts ( "Yes" ) ;
                else if ( cross (p[1] , p[2] , u ) == 0 ) {
                    if ( length ( p[1] , p[2] ) < length ( p[1] , u ) ) puts ( "No" ) ;
                    else puts ( "Yes" ) ;
                }
                else puts ( "No" ) ;
            }
            else if ( cross (p[k-1] , p[k] , u) >= 0 ) puts ( "Yes" ) ;
            else puts ( "No" ) ;
        }
    }
    return 0;
}
/*
7
(-55,0)
(-44,11)
(-22,33)
(11,22)
(0,-22)
(-22,-33)
(-44,-22)
1000
(-22,-66)
*/


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值