1. 程式人生 > >半平面交模板+又鞏固了點在直線兩側判定

半平面交模板+又鞏固了點在直線兩側判定

 

參考連結:https://blog.csdn.net/zuzhiang/article/details/78395670

https://blog.csdn.net/qq_40861916/article/details/83541403

先給道題目:poj 3335

題意:給出順時針的點,有一群人做到邊界上,要你從中找出一個點來放記分牌,使得這個點能被所有人看到,問:能不能找到?

題解:很顯然這就是讓你求多邊形的核,而求多邊形的核用半平面交就行了。

多邊形的核

平面簡單多邊形的核是該多邊形內部的一個點集,該點集中任意一點多邊形邊界上一點連線都處於這個多邊形內部

演算法簡述:

多邊形的核可以直接通過求多邊形的邊所在的直線表示的半平面的交求得。

最後一張圖片參考於:https://blog.csdn.net/acm_zl/article/details/11153475

第一個圖是有核心的,比如那個黑點,而第二個圖就不存在核心了,無論點在哪裡,總有地區是看不到的。

 

模板如下所示:

其中對於給出點的順時針和逆時針順序不同,模板需要更改 cmp函式Onright函式中的 <>

為什麼呢?給個圖你瞧瞧,我就是因為這裡卡了我一天啊,艹,以前理解得還是不夠透徹。

看 ,同樣Q點在向量P的右邊,但是因為順時針和逆時針的關係,產生的結果有著千差萬里的區別。

再回顧下 P*Q>0,表示向量P在向Q的順時針方向上;

P*Q<0,表示向量P在向量Q的逆時針方向上;

P*Q=0,表示共線。

///可以當做半平面交模板,不過有時需要在cmp函式和判斷交點是否在直線右邊時要改變下符號
///其它就沒什麼了

#include<cmath>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=111;
struct point
{
    double x,y;
    point(){}
    point(double _x,double _y){
        x=_x;y=_y;
    }
}poly[maxn];///儲存半平面交後的交點

point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p) { return point(a.x/p,a.y/p);}

bool operator < (const point &a,const point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
    if(fabs(x)<esp) return 0;
    else return x<0?-1:1;
}
bool operator == (const point &a,const point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}


double Cross(point P,point Q)
{
    return P.x*Q.y-P.y*Q.x;
}

struct LINE
{
    point s,t;
    double angle;
     LINE(){}
    LINE(point _s,point _t){
        s=_s;t=_t;
        angle=atan2(t.y-s.y,t.x-s.x);
    }
}L[maxn],dq[maxn];///儲存向量,半平面交時用到的雙端佇列dq

point Getlinenode(LINE a,LINE b)
{

    point P=a.s,v=a.t-a.s; ///點,向量
    point Q=b.s,w=b.t-b.s;
    point u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}
bool cmp(LINE P,LINE Q) ///兩向量方向相同,靠下的優先
{
    if(fabs(P.angle-Q.angle)<esp)
        return Cross(P.s-P.t,Q.s-P.t)>esp;
    return P.angle<Q.angle;
}


bool Onright(LINE L,point p){ ///判斷點p在直線L的右邊,
    return Cross(L.s-L.t,p-L.t)>esp;
}

void Half(int n,int &m)
{
    sort(L,L+n,cmp);
    int l=0,r=1;
    m=1;
    for(int i=1;i<n;++i)///篩掉同斜率,保留靠下的向量,其餘不要
        if(fabs(L[i].angle-L[i-1].angle)>esp)L[m++]=L[i];
    n=m;
    m=0;
    dq[0]=L[0],dq[1]=L[1];
    for(int i=2;i<n;++i)
    {
    if(dcmp(Cross(dq[r].s-dq[r].t,dq[r-1].s-dq[r-1].t))==0||dcmp(Cross(dq[l].s-dq[l].t,dq[l+1].s-dq[l+1].t))==0) return;
        ///交點在直線的右邊,捨去
        while(l<r&&Onright(L[i],Getlinenode(dq[r],dq[r-1]))>0)--r;
        while(l<r&&Onright(L[i],Getlinenode(dq[l],dq[l+1]))>0)++l;
        dq[++r]=L[i];
    }

    ///判斷下頭尾向量
    while(l<r&&Onright(dq[l],Getlinenode(dq[r],dq[r-1]))>0)--r;
    while(l<r&&Onright(dq[r],Getlinenode(dq[l],dq[l+1]))>0)++l;
    dq[++r]=dq[l];///將頭向量加到尾部
    for(int i=l;i<r;++i)
        poly[m++]=Getlinenode(dq[i],dq[i+1]);///儲存m+1個(範圍在[0,m])半平面交點
}
int main()
{
    int n,m,t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;++i)
            scanf("%lf%lf",&poly[i].x,&poly[i].y);
        poly[n]=poly[0];
        for(int i=0;i<n;++i)
        {
            L[i]=LINE(poly[i+1],poly[i]);
        }
        Half(n,m);
        puts(m>2?"YES":"NO");
    }
    return 0;
}

 

再來一道:題目連結:poj 3130

題意:在多邊形內找出一點,使得這點與任意頂點連線成的線段在凸邊形內,問:能否找出?

題解:很顯然也是讓你求多邊形核,直接半平面交就好了,這裡和上面有區別的是,這裡輸入的邊是按逆時針的,我們只需在上份程式碼改下cmp函式,Onright函式,以及儲存那裡改下就好了。

程式碼如下:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;

const int maxn=100;

struct point{

    double x,y;
    point(){}
    point(double _x,double _y){
        x=_x;y=_y;
    }
}poly[maxn];

point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p) { return point(a.x/p,a.y/p);}

bool operator < (const point &a,const point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x){
    if(fabs(x)<esp) return 0;
    else return x<0?-1:1;
}
bool operator == (const point &a,const point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

double Cross(point P,point Q)
{
    return P.x*Q.y-P.y*Q.x;
}

struct LINE{
    point s,t;
    double angle;

    LINE(){}
    LINE(point _s,point _t){
        s=_s;t=_t;
        angle=atan2(t.y-s.y,t.x-s.x);
    }

}L[maxn],dq[maxn];

point Getlinenode(LINE a,LINE b)
{
    point P=a.s,v=a.t-a.s;
    point Q=b.s,w=b.t-b.s;
    point u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}

bool cmp(LINE P,LINE Q){

    if(fabs(P.angle-Q.angle)<esp)
        return Cross(P.t-P.s,Q.s-P.s)<0;
    return P.angle<Q.angle;
}

bool Onright(LINE a,point p){
    return Cross(a.t-a.s,p-a.s)<0;
}

void Half(int n,int &m)
{
    sort(L,L+n,cmp);
    int l=0,r=1;
    m=1;
    for(int i=1;i<n;i++)
        if(fabs(L[i].angle-L[i-1].angle)>esp) L[m++]=L[i];
    n=m;

    m=0;
    dq[0]=L[0],dq[1]=L[1];

    for(int i=2;i<n;i++)
    {
        if(dcmp(Cross(dq[r].s-dq[r].t,dq[r-1].s-dq[r-1].t))==0||dcmp(Cross(dq[l].s-dq[l].t,dq[l+1].s-dq[l+1].t))==0) return;
        while(l<r&&Onright(L[i],Getlinenode(dq[r],dq[r-1]))>0) r--;
        while(l<r&&Onright(L[i],Getlinenode(dq[l],dq[l+1]))>0) l++;
        dq[++r]=L[i];
    }

    while(l<r&&Onright(dq[l],Getlinenode(dq[r],dq[r-1]))>0) r--;
    while(l<r&&Onright(dq[r],Getlinenode(dq[l],dq[l+1]))>0) l++;

    dq[++r]=dq[l];

    for(int i=l;i<r;i++)
        poly[m++]=Getlinenode(dq[i],dq[i+1]);
}

int main()
{
    int n,m,t;

    while(scanf("%d",&n)&&n)
    {


        for(int i=0;i<n;i++)
            scanf("%lf%lf",&poly[i].x,&poly[i].y);
        poly[n]=poly[0];

        for(int i=0;i<n;i++)
            L[i]=LINE(poly[i],poly[i+1]);
        Half(n,m);

        puts(m>2?"1":"0");

    }
    return 0;
}

 

我的標籤:做個有情懷的程式設計師。