zoj 1081

这个题目做了很久,一直wa。后来发现,原来多边形可能不是凸多边形,一下子醒过来了。既然不是凸多边形,那么面积法和叉积法都不能用了,看到网上有说用射线法的,也就把这个题目当做学习射线法的例题了。判断点是否在多边形内部(并非凸都变形),射线法的算法流程大致如下:

       由被测点做射线,计算这条射线与多边形边的交点的个数如果是奇数,则说明点再多边形的内部,如果是偶数,说明点再多边形的外部。但是遇到如果射线与多边形的顶点相交的情况,需要太特殊的处理。此时可以用到线段非规范相交的模板,黑书上有这个模板。这个题目是个模板题目,还得回过头来好好看看。这个算法真的很优美。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
#define N 103
#define eps 1e-10
struct point
{
    double x;
    double y;
}p[N];
struct beeline
{
    point a,b;
};
int n;
bool dy(double x,double y)
{
    return x>y+eps;
}
bool xy(double x,double y)
{
    return x<y-eps;
}
bool dyd(double x,double y)
{
    return x>y-eps;
}
bool xyd(double x,double y)
{
    return x<y+eps;
}
bool dd(double x,double y)
{
    return fabs(x-y)<eps;
}
double det(double x1,double y1,double x2,double y2)
{
    return x1*y2-x2*y1;
}
double cross(point a,point b,point c)
{
    return det(c.x-a.x,c.y-a.y,b.x-a.x,b.y-a.y);
}
bool onSegment(point a,point b,point c)//判断点是否在射线上边
{
    double maxx=max(a.x,b.x);  //找个多边形的x,y的边界值
    double maxy=max(a.y,b.y);
    double minx=min(a.x,b.x);
    double miny=min(a.y,b.y);
    //判断点c是否在射线上所在的直线上 如果在 判断点c是否在多边形的区域内,如果成了则点c再射线上
    if(dd(cross(a,b,c),0.0)&&dyd(c.x,minx)&&xyd(c.x,maxx)&&dyd(c.y,miny)&&xyd(c.y,maxy))
      return true;
    return false;
}
bool segIntersect(point p1,point p2,point p3,point p4)
{
    double d1=cross(p3,p4,p1);
    double d2=cross(p3,p4,p2);
    double d3=cross(p1,p2,p3);
    double d4=cross(p1,p2,p4);
    if(xy(d1*d2,0.0)&&xy(d3*d4,0.0))return true;  //如果规范相交
    if(dd(d1,0.0)&&onSegment(p3,p4,p1))return true;//如果非规范相交
    if(dd(d2,0.0)&&onSegment(p3,p4,p2))return true;
    if(dd(d3,0.0)&&onSegment(p1,p2,p3))return true;
    if(dd(d4,0.0)&&onSegment(p1,p2,p4))return true;
    return false;
}
bool inPolygon(point pot)
{
    int count=0;
    beeline l;
    l.a=pot;
    l.b.y=pot.y;
    l.b.x=1e10;
    p[n]=p[0];
    for(int i=0;i<n;i++)
    {
        if(onSegment(p[i],p[i+1],pot))return true;
        if(!dd(p[i].y,p[i+1].y))  //标记不与x轴平行
        {
            int tmp=-1;
            if(onSegment(l.a,l.b,p[i]))
               tmp=i;
            else if(onSegment(l.a,l.b,p[i+1]))
               tmp=i+1;
            if(tmp!=-1&&dd(p[tmp].y,max(p[i].y,p[i+1].y)))
               count++;
            else if(tmp==-1&&segIntersect(p[i],p[i+1],l.a,l.b))
               count++;
        }
    }
    if(count%2==1)return true;
    else return false;
}
int main()
{
    int m;
    int cn=1;
    point pot;
    while(scanf("%d",&n)&&n)
    {
        scanf("%d",&m);
        if(cn!=1)printf("\n");
        for(int i=0;i<n;i++)
        {
            scanf("%lf%lf",&p[i].x,&p[i].y);
        }
        printf("Problem %d:\n",cn++);
        while(m--)
        {
            scanf("%lf%lf",&pot.x,&pot.y);
            if(inPolygon(pot))printf("Within\n");
            else printf("Outside\n");
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值