GYM 101128 J.Saint John Festival(凸包-Graham扫描法+二分)

85 篇文章 0 订阅
81 篇文章 0 订阅

Description
给出l个大点和s个小点,问有多少小点被三个大点组成的三角形覆盖
Input
第一行一整数l表示大点的数量,之后l行每行两个整数x,y表示该大点的横纵坐标,然后输入一整数s表示小点数量,最后s行每行两个整数x和y表示该小点的横纵坐标(3<=l<=10000,1<=s<=50000,0<=x,y<=2^30)
Output
输出满足条件的小点的数量
Sample Input
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
Sample Output
3
Solution
只要一个小点在由这些大点组成的凸包中那么一定存在三个大点组成一个三角形覆盖这个小点,所以首先对l个大点用Graham扫描法求一遍凸包,以0点为三角形一顶点,相邻两凸包上点作为另外两个顶点,这样就对整个凸包进行了三角剖分,之后对于每个小点x,二分其属于哪个三角形,假设其属于0,i,j=i+1三点组成的三角形,那么ox^oi和ox^oj(^表示叉乘)必然异号,那么x就在oi和oj为两个方向的锥内,之后还要判断x点是否在三角形oij里,这时判断向量ix是否在向量ij的逆时针方向即可
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 11111
const double eps=1e-8;
const double PI=acos(-1.0);
struct Point
{
    double x,y;
    double k;
    Point(){}
    Point(double _x,double _y)
    {
        x=_x;y=_y;
    }
    Point operator -(const Point &b)const
    {
        return Point(x-b.x,y-b.y);
    }
    double operator ^(const Point &b)const
    {
        return x*b.y-y*b.x;
    }
    double operator *(const Point &b)const
    {
        return x*b.x+y*b.y;
    }
};
int sgn(double x)
{
    if(fabs(x)<eps) return 0;
    if(x<0) return -1;
    else return 1;
}
double dist(Point a,Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
Point list[maxn];
int Stack[maxn],top;
//相对于list[0]的极角排序
bool _cmp(Point p1,Point p2)
{
    double tmp=(p1-list[0])^(p2-list[0]);
    if(sgn(tmp)>0)return true;
    else if(sgn(tmp)==0&&sgn(dist(p1,list[0])-dist(p2,list[0]))<=0)return true;
    else return false;
}
void Graham(int n)
{
    Point p0;
    int k=0;
    p0=list[0];
    //找最下边的一个点
    for(int i=1;i<n;i++)
    {
        if((p0.y>list[i].y)||(p0.y==list[i].y&&p0.x>list[i].x))
        {
            p0=list[i];
            k=i;
        }
    }
    swap(list[k],list[0]);
    sort(list+1,list+n,_cmp);
    if(n==1)
    {
        top=1;
        Stack[0]=0;
        return;
    }
    if(n==2)
    {
        top=2;
        Stack[0]=0;
        Stack[1]=1;
        return ;
    }
    Stack[0]=0;
    Stack[1]=1;
    top=2;
    for(int i=2;i<n;i++)
    {
        while(top>1&&sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]]))<=0)
            top--;
        Stack[top++]=i;
    }
}
int L,S;
Point s;
bool check(Point s)
{
    int l=1,r=top-2;
    while(l<=r)
    {
        int mid=(l+r)/2;
        Point a=list[Stack[0]],b=list[Stack[mid]],c=list[Stack[mid+1]];
        double t1=(b-a)^(s-a);
        double t2=(c-a)^(s-a);
        if(t1>=0&&t2<=0)
        {
            double t=(c-b)^(s-b);
            if(t>=0)return 1;
            return 0;
        }
        if(t1<0)r=mid-1;
        else l=mid+1;
    }
    return 0;
}
int main()
{
    while(~scanf("%d",&L))
    {
        for(int i=0;i<L;i++)scanf("%lf%lf",&list[i].x,&list[i].y);
        Graham(L);
        int ans=0;
        scanf("%d",&S);
        while(S--)
        {
            scanf("%lf%lf",&s.x,&s.y);
            if(check(s))ans++;
        }
        printf("%d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值