【USACO题库】3.4.1 Closed Fences闭合的栅栏

题意

平面上有按逆时针顺序排好的N个点,给你这些点的坐标。
首先要判断这N个点按顺序连边构成的是否是一个合法的多边形。
一个合法的多边形定义为多边形的边与边互不交。
然后再给一个点(观察点)的坐标,求这个点能看到多边形上的哪些边。
如此图:

(x,y)为观察点,能看到
(x1,y1)~(x6,y6),(x6,y6)~(x5,y5),(x4,y4)~(x3,y3)

分析

判断多边形合法其实就只用暴力枚举每两条边,判断这两条线段是否相交。(注意是规范相交

线段规范相交

判断线段相交有很多方法,最简单的是用向量的叉积
因为两条线段规范相交等价于:
第一条线段的两个端点在第二条线段两侧 且 第二条线段的两个端点在第一条线段两侧
所以我们分别从 线段1的端点1向线段2的端点1,2 和 线段1的端点2作叉积。叉积相乘<0则线段规范相交。

第二问怎么办?
如果能看到某条线段等价于从观察点向线段上的某个点连线不会与多边形的其他边相交(非规范相交)。

非规范相交

当一条线段至少有一端点在另一条线段上,则称这两条线段非规范相交。
由规范相交判定方法可看出,非规范相交某些情况是叉积相乘=0的。
但如果就把叉积相乘<0改成=0,会把一些不相交的例子误判为非规范相交。(例如两条线段在同一直线上,但不相交)
设A在BC上,非规范相交即为:

  1. A在BC所在直线上(即叉积AB*AC=0)
  2. A在BC范围内

于是我们可以通过坐标比较来判断 2 是否成立。

double cheng(double x,double mini,double maxi)
{
    return (x-mini)*(x-maxi);
}
double between(point p1,point p2,point p3)
{
    if(abs(p2.x-p3.x)>abs(p2.y-p3.y))
        return cheng(p1.x,min(p2.x,p3.x),max(p2.x,p3.x));
    else
        return cheng(p1.y,min(p2.y,p3.y),max(p2.y,p3.y));
}
int cross(point p1,point p2,point p3,point p4)
{
     double t1=cha(p2.x-p1.x,p2.y-p1.y,p3.x-p1.x,p3.y-p1.y),
            t2=cha(p2.x-p1.x,p2.y-p1.y,p4.x-p1.x,p4.y-p1.y),
            t3=cha(p4.x-p3.x,p4.y-p3.y,p1.x-p3.x,p1.y-p3.y),
            t4=cha(p4.x-p3.x,p4.y-p3.y,p2.x-p3.x,p2.y-p3.y);
     if(t1*t2<0 && t3*t4<0) return 1;//规范相交
     if(t1==0 && between(p3,p1,p2)<=0 ||
        t2==0 && between(p4,p1,p2)<=0 ||
        t3==0 && between(p1,p3,p4)<=0 ||
        t4==0 && between(p2,p3,p4)<=0) return 2;//非规范相交
     return 0;//不相交
}

但是真的要枚举线段上的每个点吗?

二分

枚举每条线段。
首先从线段的两个端点开始设为left点和right点,求出mid点。
判断从观察点向mid的连线是否与其它线段相交。是则返回false。
继续二分(left,mid)和(mid,right),直到某个left与right的直线距离<=1e-3,返回true。

bool find(point p1,point p2,int bz)
{
     point mid;
     mid.x=(p1.x+p2.x)/2,mid.y=(p1.y+p2.y)/2;
     if(dis(p1,mid)<=Min || dis(mid,p2)<=Min) return 0;
     bool p=1;
     fo(i,1,n)
         if(i!=bz && cross(a[0],mid,a[i],a[i%n+1])>0)
         {
             p=0;
             break;
         }
     if(p) return 1;
     return find(p1,mid,bz) || find(mid,p2,bz);
}

这道题曾困了我半年(太弱了QAQ),有过许多不同的想法,最后才用二分A了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值