半平面交讲解 - N^2写法

 ps:如果这是第一次接触半平面交,不要认为它是很个高深的东东.其实以你现在的几何水平完全能够YY写出它.


半平面: 对一条直线,它将整个平面分成了两部分,对于其中的每一部分.就叫做一个半平面.(这个应该好理解吧)


现在我们要保留其中一个半平面,  可以用向量的左边,右边来标记,  还可以用  Ax+By+C 与 0的关系来标记(这里你千万别纠结它是在直线上方呢,还是下方,而让自己糊涂了),对于上面的两种剖分平面的方式, 你当然还可以用其他的条件,比如:    Ax+By+C  与  10 的关系, 我举这个例子只是想说明:

         你只要给出一个条件就行, 对于该条件必有成立与不成立两部分,这样就把整个平面分成了两部分了.

当然我们一般用的上面的两种..


废话说了一大托..


赶紧把这个秒了:

       题目: 给一个凸多边形,然后给你一个向量,求该向量左边部分与凸多边形的交集所形成的多边形,然后得出该多边形的点集.


很水吧?不会?再好好想想.......


用这条向量与多边形的每个点判断是否在该向量的左边,每条边是否与该向量所在的直线产生了交点..


有想法了么? 相信只是一些细节没太清楚了?


其实下面的稍微浏览下就行了,,,半平面交N^2算法,你会发现真得很水.


半平面交N^2算法:

        对凸多边形,选定一个方向,,对第i个点判断是否满足该 向量的条件,如果满足,把该点加入点集, 然后看i 到i+1这条线段与那条直线是否有交点.有的话加入就行.


ok! 就完了,,,说的一个大概,,你想想自己会怎么写,然后看下下面的写法,综合一下,你就搞定了


#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<cmath>
#define zero(x)((((x)>0?(x):(-(x)))<eps))
using namespace std;
double eps=1e-8;
double pi=acos(-1);
struct P
{
    double x,y;
    void read(){scanf("%lf%lf",&x,&y);}
    P(){}
    P(double x,double y):x(x),y(y){}
    P operator +(const P& p){return P(x+p.x,y+p.y);}
    P operator -(const P& p){return P(x-p.x,y-p.y);}
    P operator /(double d)  {return P(x/d,y/d);}
    P operator *(double d)  {return P(x*d,y*d);}
    P rot(double th){return P(x*cos(th)-y*sin(th),x*sin(th)+y*cos(th));}
    double operator *(const P p){return x*p.y-y*p.x;}
    bool operator !=(const P& p){return (zero(x-p.x)&&zero(y-p.y))==false;}
};
struct Line
{
    P a,b;
    Line(){}
    Line(P a,P b):a(a),b(b){}
};
P tp[305],pp[305];
//pp :记录最终的点集 , tp:临时存储用的
int N;//记录点集的数目
P ins_seg(P a1,P b1,P a2,P b2)//直线求交
{
    double u=(b1-a1)*(a2-a1);
    double v=(a1-b1)*(b2-b1);
    return (a2 * v + b2 * u ) / ( u + v );
}
void cut(P a,P b)// 用一条有向向量对多变形进行切割,这里是保留向量的右边
{
   int dn=0;
   double t1,t2;
   for(int i=0;i<N;i++)// 枚举每个点
   {
       t1=(b-a)*(pp[i]-a);// 叉积第i个点 
       t2=(b-a)*(pp[(i+1)%N]-a);//叉积第i+1个点
       if(t1<eps)tp[dn++]=pp[i];// 第i个点在向量右边,加入点集中
       if(t1*t2<-eps)tp[dn++]=ins_seg(a,b,pp[i],pp[(i+1)%N]);// i到i+1的线段与该向量相交(乘积为负,表明两点不在同一边,必然相交)
   }
   N=0;
   for(int i=0;i<dn;i++)// 把临时存储的点集传给pp里面
   if(N==0||pp[N-1]!=tp[i])// 去掉重点..
   pp[N++]=tp[i];
}
三道水题:   赶紧写了,测模板

POJ 3335 Rotating Scoreboard
http://acm.pku.edu.cn/JudgeOnline/problem?id=3335

POJ 1474 Video Surveillance
http://acm.pku.edu.cn/JudgeOnline/problem?id=1474

POJ 1279 Art Gallery
http://acm.pku.edu.cn/JudgeOnline/problem?id=1279


POJ 3525 Most Distant Point from the Sea (推荐)
http://acm.pku.edu.cn/JudgeOnline/problem?id=3525

备注:题解是白色字体(方便你先思考这个问题)

我当时写这题反正二了,没有想到二分,当时用的枚举第i条,然后切割出在所有边中,到改边是最近的点集集合,这里用角平分线就行切割的,反正我当时写了很久..自己YY如何求角平分线..

正解二分:关键是想到问题等同在该多边形内放一个最大的圆,求半径是多少,余下的你懂的..



POJ 1755 Triathlon (推荐)
http://acm.pku.edu.cn/JudgeOnline/problem?id=1755

备注:题解白色字体

这题,我只想问,你最后过的时候eps取的是多少..反正我跪了..用的 1e-16过的...我同学是 1e-8过的..



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值