hdu 6590 Code (判断两凸包是否相交)19_hdu多校

题目链接:

 

参考博客:

题意:

给定一组(x1,x2,y),其中y为1或0,问是否有一组(w1,w2,b),使得上述的每一个(x1,x2,y)都满足x1*w1+x2*w2+b在y=1时大于0,在y=-1时小于0.

题解:我们可以将(x1,x2)看成是坐标点,那么题目就可看成,能否找出一条直线,将1点与-1点分开。

也就是我们可以求1点的凸包以及-1点的凸包。然后我们再判断这两个凸包是否相交。假如不相交,说明存在这样一条直线。

 

对于求两凸包是否相交,我们可以判断一个凸包上的点是否在另一个凸包里面,假设都不在,说明这两凸包不相交。

判断点是否在凸包里面,我们可以二分,找到一条线段,使得该点被夹在这条线段之间,那么此时如果该点在这条线段的左边,说明该点在该凸包里面。

 

#include<bits/stdc++.h>


using namespace std;
const int N=105;

typedef long long LL;
struct point{
    LL x,y;
    point(){}
    point(LL _x,LL _y){
        x=_x;y=_y;
    }
}date[2][N],aim[2][N];

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,int 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;
}

LL Cross(point a,point b) { return a.x*b.y-a.y*b.x;}
double Length(point a) { return sqrt(a.x*a.x*1.0+a.y*a.y*1.0);}

bool cmp(point a,point b) ///坐标排序
{
    return (a.y<b.y||(a.y==b.y&&a.x<b.x));
}

int andrew(point *p,int n,point *ch)
{

    sort(p,p+n,cmp);

   int tot=-1;
    for(int i=0;i<n;i++) ///构造凸包下侧
    {
        while(tot>0&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
            tot--;
        ch[++tot]=p[i];
    }

    for(int i=n-2,k=tot;i>=0;i--){ ///构造凸包上侧
        while(tot>k&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
            tot--;
        ch[++tot]=p[i];
    }
    ///区间在[0,tot],ch[0]=ch[tot];
    return tot;
}

///二分判断点A是否在凸包p中
bool check(point A,point *p,int n)
{

    ///r=n-1,因为0点与n点是同样的点
    int l=0,r=n-1,mid;
    while(l<=r)
    {
        mid=(l+r)>>1;
        LL a1=Cross(p[mid]-p[0],A-p[0]);
        LL a2=Cross(p[mid+1]-p[0],A-p[0]);

        if(a1>=0&&a2<=0)
        {
        ///当某个点被夹在线段mid-(mid+1)之间,假如点在向量mid-(mid+1)左边
        ///说明该点在该凸包里
            if(Cross(p[mid+1]-p[mid],A-p[mid])>=0) return true;
            return false;
        }
        ///往下找,能到这一步,说明p[mid],p[mid+1]都在点A上面、
        ///因为我们需要一正一负,故要往下找
        else if(a1<0)
            r=mid-1;
        else l=mid+1; ///往上找,能到这一步,说明p[mid],p[mid+1]都在点A下面
    }
    return false;
}

int main()
{

       int ncase;
       scanf("%d",&ncase);

       int num0[2],num1[2];
       while(ncase--)
       {
           ///num1存储1点,num0存储-1点
            num0[0]=num0[1]=num1[0]=num1[1]=0;
            memset(date,0,sizeof(date));
            memset(aim,0,sizeof(aim));

            int n;
            scanf("%d",&n);
            for(int i=1;i<=n;i++)
            {
                LL x,y,z;
                scanf("%lld%lld%lld",&x,&y,&z);
                if(z==1) date[1][num1[0]++]=point(x,y);
                else date[0][num0[0]++]=point(x,y);
            }
            ///分别给1点和-1点求凸包
            num0[1]=andrew(date[0],num0[0],aim[0]);
            num1[1]=andrew(date[1],num1[0],aim[1]);

//        }
            bool flag=false;

            ///检查凸包-1点,此点是否在凸包1点中
            for(int i=0;i<num0[1];i++)
                flag|=check(aim[0][i],aim[1],num1[1]);
            ///检查凸包1点,此点是否在凸包-1点中
            for(int i=0;i<num1[1];i++)
                flag|=check(aim[1][i],aim[0],num0[1]);

            if(!flag) puts("Successful!");
            else puts("Infinite loop!");

       }

        return 0;
}

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值