【POJ2318】TOYS(点在凸多边形内判定---模版暴力/二分)

题目地址:http://poj.org/problem?id=2318

题目:


给出长方形的左上角(x1,y1)右下角(x2,y2),n块木板把这个长方形分割成n+1个区域(给出木板的左上和右下坐标),m个玩具,给出这个m个玩具的具体坐标,忽略玩具的体积,问0-n这个n+1个区域,每个区域内的玩具数。

注意:玩具不会落在两个区域的公共边上,区域不相交

 

解题思路:


不要用点在多边形内判定的模版,很可能会超时,这个多边形是个凸多边形,直接判定这个点是否都在边的左侧即可,注意边的方向是顺时针的!!玩具不会落在木板上!且不会超出长方形区域!

(1)直接暴力跑了1.8ms,在超时的边缘试探

(2)二分:从左到右找到第一个在toy坐标右边的木板,这个toy就位于该木板左侧的区域内,时间快很多

 

ac代码:


暴力:

#include<iostream>
#include <vector>
#include<math.h>
using namespace std;
const int maxn = 5005;
const double pi = acos(-1.0);
const double eps = 1e-10;
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};
typedef Point Vector;

Vector operator - (Vector a, Vector b)//向量减法
{
    return Vector(a.x - b.x, a.y - b.y);
}
int dcmp(double x)//精度三态函数(>0,<0,=0)
{
    if (fabs(x) < eps)return 0; //等于
    else return x < 0 ? -1 : 1;//小于,大于
}
double Cross(Vector a, Vector b)//外积
{
    return a.x * b.y - a.y * b.x;
}

struct node{
    int cnt;
    vector<Point> ps;
    node(int cnt = 0):cnt(cnt) {ps.clear(); }
}ans[maxn];

int n, m, x_1, y_1, x_2, y_2, ui, li;
Point toy[maxn];

int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    int line = 0;
    while(scanf("%d", &n))
    {
        if(!n) break;
        if(line) printf("\n");
        line = 1;
        scanf("%d %d %d %d %d", &m, &x_1, &y_1, &x_2, &y_2);
        for(int i = 0; i < maxn; i++) ans[i].ps.clear(), ans[i].cnt = 0;
        //左上a, 右下b
        ans[0].ps.push_back(Point(x_1, y_1));
        ans[0].ps.push_back(Point(x_1, y_2));
        for(int i = 0; i < n; i++)
        {
            //(Ui,y1) and (Li,y2)
            scanf("%d %d", &ui, &li);
            //b,a
            ans[i].ps.push_back(Point(li, y_2));
            ans[i].ps.push_back(Point(ui, y_1));
            //下一个区间a,b
            ans[i+1].ps.push_back(Point(ui, y_1));
            ans[i+1].ps.push_back(Point(li, y_2));
        }
        //b,a
        ans[n].ps.push_back(Point(x_2, y_2));
        ans[n].ps.push_back(Point(x_2, y_1));
        for(int k = 0; k < m; k++)
        {
            scanf("%lf %lf", &toy[k].x, &toy[k].y);
            for(int i = 0; i <= n; i++)
            {
                int in = 1;
                for(int j = 0; j < 4; j++) // 多边形是凸多边形,不需复杂判断,反而可能会超时,包括toy在上下边界的情况
                {
                    int cross = dcmp(Cross(ans[i].ps[(j+1)%4] - ans[i].ps[j], toy[k] - ans[i].ps[j]));
                    if(cross < 0) in = 0;
                }
                if(in)
                {
                    ans[i].cnt++;
                    break;
                }
            }
        }
        for(int i = 0; i <= n; i++)
        {
            printf("%d: %d\n", i, ans[i].cnt);
        }
    }
    return 0;
}

二分:(参考博客:https://blog.csdn.net/qq_36782366/article/details/77899164

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const double eps=1e-10;
int dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    return x<0?-1:1;
}
const int maxn=5000+10;
struct Point
{
    double x,y;
    Point(){}
    Point(double x,double y):x(x),y(y){}
};
typedef Point Vector;
Vector operator-(Point A,Point B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
double Dot(Vector A,Vector B)
{
    return A.x*B.x+A.y*B.y;
}
double Cross(Vector A,Vector B)
{
    return A.x*B.y-A.y*B.x;
}
 
Point p[maxn];//需要判断位置的所有点
Point up[maxn],down[maxn];//记录上一排的n+1个点,和下一排的n+1个点
Point poly[maxn][4];
int ans[maxn];//保存第i个隔间有几个点
 
int main()
{
    int n,m;
    double x1,y1,x2,y2;
    bool first=true;
    while(scanf("%d",&n)==1 && n)
    {
        if(!first) printf("\n");
        first=false;
 
        scanf("%d%lf%lf%lf%lf",&m,&x1,&y1,&x2,&y2);
        up[0]=Point(x1,y1);
        up[n+1]=Point(x2,y1);
        down[0]=Point(x1,y2);
        down[n+1]=Point(x2,y2);
        for(int i=1;i<=n;++i)
        {
            scanf("%lf%lf",&x1,&x2);
            up[i]=Point(x1,y1);
            down[i]=Point(x2,y2);
        }
        memset(ans,0,sizeof(ans));
 
        for(int i=1;i<=m;++i)
        {
            double x,y;
            scanf("%lf%lf",&p[i].x,&p[i].y);
            int l = 1,r = 1+n;
            int tmp;
            while( l <= r)
            {
                int mid = (l + r)/2;
                if(dcmp (Cross(up[mid]-down[mid], p[i]-down[mid]) )>0)
                {
                    tmp = mid;
                    r = mid - 1;
                }
                else l = mid + 1;
            }
            ans[tmp-1]++;
        }
        for(int i=0;i<=n;++i)
            printf("%d: %d\n",i,ans[i]);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值