Hust oj 1429 凸多邊形(叉乘+二分)

凸多邊形
Time Limit: 2000 MS Memory Limit: 65536 K
Total Submit: 276(61 users) Total Accepted: 101(52 users) Rating:  Special Judge: No
Description

已知一個凸多邊形A(包含n個點,點按照順時針給出),和一個點集B(包含m個點),請判斷這m個點是否都嚴格在凸多邊形A內部。

Input

輸入包含多組測試數據。

對於每組測試數據:

1行,包含一個整數n (3 ≤ n ≤ 105)代表着凸多邊形A的點的數量。

接下來n行每行包含一個座標(x, y) (-109 ≤ x, y ≤ 109表示這個凸多邊形,點按照順時針給出。

n + 2行,包含一個整數m (3 ≤ m ≤ 105)代表着點集B的點的數量。

接下來m行每行包含一個座標(x, y) (-109 ≤ x, y ≤ 109表示這個點集B

處理到文件結束

Output

對於每組測試數據:

1行,如果點集B都嚴格在凸多邊形A內,輸出YES,否則輸出NO

Sample Input

4

-10 -10

-10 10

10 10

10 -10

3

0 0

1 1

2 2

4

-10 -10

-10 10

10 10

10 -10

3

100 100

1 1

2 2

Sample Output

YES

NO


正常暴力用叉乘跑的話肯定會超時,這裏學習到了一種新姿勢解決這類問題

計算幾何之判斷點是否在多邊形內,

判斷點是否在多邊形內有多種方法:射線法,角度和判斷法,改進弧長法還有這次用到的二分法。

前三者的時間複雜度均爲O(n),此方法複雜度僅爲O(logn)。

而且對於判斷很多點是否在多邊形內,就可以用這種方法了,耗時少。

原理:

將一個多邊形,以其中一個點爲原點,開始與其他各點相連並延長做射線,則會形成許多個三角形區域。(如左圖)

 

這樣我們可以先判斷點在哪兩條向量之間。用二分查找,可以很快搜索到。

當然,首先要判斷點是否在最左邊向量左側或者最右邊向量右側,如是,則點不在多邊形內。

以右圖爲例,我們找到紫色點在左數第一個三角形區域內,綠色點在左數第二個三角形區域內。

然後,再判斷下圖所示線段與 所判斷點的位置關係。

 

綠色的線段可以判斷綠色的點,左邊紫色的點也由相應的線段來判斷位置關係。

這樣可以判斷點是否在多邊形內啦。

總結一下:

①建立一個個三角形區域,用其中兩條邊判斷點所在大體區域。

②用第三條邊來判斷點是否在多邊形內。

二分查找就是用在了第一條的地方,用來查找大體區域位置。

明白了這個,就可以做相應的題目來練習一下了!

就是這道題~。~

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;

typedef long long LL;
const int Maxn = 100005;
struct Point
{
    LL x;
    LL y;
}a[Maxn],b[Maxn];
LL n,m;

LL cross(Point p0,Point p1,Point p2)
{
    return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y);
}

int main()
{
    while(~scanf("%lld",&n))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%lld%lld",&a[i].x,&a[i].y);
        }
        scanf("%lld",&m);
        for(int i=0;i<m;i++)
        {
            scanf("%lld%lld",&b[i].x,&b[i].y);
        }
        int flag = 0;
        for(int i=0;i<m;i++)
        {
            if(cross(a[0],a[1],b[i]) >= 0 || cross(a[0],a[n-1],b[i]) <= 0)
            {
                flag = 1;
                break;
            }
            int left = 1;
            int right = n - 1;
            while(left < right)
            {
                int mid = (right + left) >> 1;
                if(cross(a[0],a[mid],b[i]) > 0)
                    right = mid;
                else
                    left = mid + 1;
            }
            if(cross(a[left],a[left-1],b[i]) <= 0)
            {
                flag = 1;
                break;
            }
        }
        if(flag)
            printf("NO\n");
        else
            printf("YES\n");
    }
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章