Gym - 101128J (凸包+二分)

題意:問有多少個黑點在任意三個紅點組成的三角形內
題解:必然一眼能看出是求點在紅色點組成的凸包內吧。然後暴力枚舉黑點與凸包判叉積的話會T,所以有一個優化是,將凸包分成x個三角形(固定一個起點,枚舉任意兩個相鄰的點),然後進行二分,找到一個叉積>=0 一個<=0的時候,再判斷是否在第三條邊左側就ok了(因爲在凸包外也有滿足叉積>=0 && 叉積<=0的情況)


#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <set>
#include <cmath>
#include <map>
#include <sstream>
#define LL long long
#define INF 0x3f3f3f3f
#define mod 1000000007
#define eps 1e-8
const int maxn = 200000 + 5;
using namespace std;
struct Point
{
    double x;
    double y;
    Point(){}
    Point(double xx, double yy):x(xx),y(yy){}
}p[maxn],pt[maxn];
vector<Point> vec;
//小於0表示p2在p0p1右側
double cross(Point p0, Point p1, Point p2)
{
    return (p1.x - p0.x)*(p2.y - p0.y) - (p2.x - p0.x)*(p1.y - p0.y);
}

//計算距離
double dis(Point p1, Point p2)
{
    return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}

bool cmp(const Point &p1, const Point &p2)
{
    double temp = cross(p[0], p1, p2);
    if (fabs(temp) < 1e-6)//極角相等按照距離從小到大排序
    {
        return dis(p[0], p1) < dis(p[0], p2);
    }
    else
    {
        return temp > 0;
    }
}

vector<Point> graham_scan(int n)
{
    vector<Point> ch;
    int top = 2;
    int index = 0;
    for (int i = 1; i < n; ++i)//選出Y座標最小的點,若Y座標相等,選擇X座標小的點
    {
        if (p[i].y < p[index].y || (p[i].y == p[index].y && p[i].x < p[index].x))
        {
            index = i;
        }
    }
    swap(p[0], p[index]);
    ch.push_back(p[0]);
    //按極角排序
    sort(p + 1, p + n, cmp);
    ch.push_back(p[1]);
    ch.push_back(p[2]);
    for (int i = 3; i < n; ++i)
    {
        while (top > 0 && cross(ch[top - 1], p[i], ch[top]) >= 0)
        {
            --top;
            ch.pop_back();
        }
        ch.push_back(p[i]);
        ++top;
    }
    return ch;
}
bool solve(Point pp, vector<Point> vec){
    int size = (int)vec.size();
    Point p0 = vec[0];
    int l = 1, r = size-2;
    while(l <= r){
        int m = (l+r)/2;
        double flag1 = cross(p0,vec[m],pp);
        double flag2 = cross(p0,vec[m+1],pp);
        //flag1 < 0 表示 pp 在 p0 vec[m+1] 右側
        if(flag1 >= 0 && flag2 <= 0){
            double flag3 = cross(vec[m],vec[m+1],pp);
            if(flag3 >= 0)
                return true;
            return false;
        }
        if(flag1 < 0){
            r = m - 1;
        }
        else{
            l = m + 1;
        }
    }
    return false;
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=0; i<n; i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
    vec = graham_scan(n);
    int m;
    scanf("%d",&m);
    for(int i=0; i<m; i++)
        scanf("%lf%lf",&pt[i].x,&pt[i].y);
    int ans = 0;
    for(int i=0; i<m; i++){
        if(solve(pt[i], vec)){
            ans++;
        }
    }
    printf("%d\n",ans);
}
/*
 8 
 3 4
 2 8
 5 4
 1 8
 4 7
 3 10
 11 2
 7 3
 6
 5 12
 3 7
 3 3
 4 5
 0 4
 2 6
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章