卷包裹,线段相交,计算几何(找边界,LA 3218)

成吨的细节。。。


就是找右转得最厉害的线段,然后沿着这个线段跑到最近的路口。


那如何找右转得最厉害的线段呢?就是枚举所有可能的下一个点,然后取其中最优的那个即可。

那如何判断哪个最优呢?


我是分类讨论

设上一个点是LAST,这一个点是NOW。

那么我目前的朝向就是V=NOW-LAST。

设两个点A,B。

如果A,B分别是左转和右转,那就选B。

否则如果A扭头,我就选B。

否则如果B扭头,我就选A。

否则如果B相对A右转,我就选B。

否则我选A。


那如何找最近的路口呢?就是枚举路上的所有路口,然后取其中最近的那个即可。

那如何判断最近呢?

直接两点间距离公式即可。


主要是难在很多琐碎的细节需要讨论清楚,否则就会找错方向和路口,从而出错或者陷入死循环。

debug浪费了非常多的时间,如果能完全想清楚再下手写代码就好了。

就是仅仅有大致思路是不够的,尽量把实现的细节都完全弄明白,具体到这个功能块每一步该怎么做,可以拿张纸写下具体过程,然后证明正确了再动手写代码。否则等到你写的时候就会比较无暇顾及细节,容易出错,而且没有那种对程序精细的掌控感。一旦出bug的话就会很不知所措,只能慢吞吞地单步调试。


再次通过自己带数据查出bug。这个方法真的很强.。但是这毕竟治标不治本。这方法不但速度有点慢,而且还得看运气。想办法提升自己的代码AC率才是提升自己的根本方法。既要学会使用技巧也要想办法提升自己。


代码

#include<bits/stdc++.h>
using namespace std;

struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
    void Read()
    {
        scanf("%lf %lf",&x,&y);
    }
    bool operator < (const Point& rhs) const
    {
        return x<rhs.x||(x==rhs.x&&y<rhs.y);
    }
};

typedef Point Vector;

const double eps = 1e-10;
int dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    return x<0?-1:1;
}

Point operator + (Point A,Vector B)
{
    return Point(A.x+B.x,A.y+B.y);
}

Vector operator - (Point A,Point B)
{
    return Vector(A.x-B.x,A.y-B.y);
}

Vector operator * (Vector V,double t)
{
    return Vector(V.x*t,V.y*t);
}

bool operator == (Point A,Point B)
{
    return dcmp(A.x-B.x)==0&&dcmp(A.y-B.y)==0;
}

bool operator != (Point A,Point B)
{
    return dcmp(A.x-B.x)!=0||dcmp(A.y-B.y)!=0;
}

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

double Len(Vector V)
{
    return sqrt(Dot(V,V));
}

double Dist(Point A,Point B)
{
    return Len(A-B);
}

Point GetIntersection(Point A,Point B,Point C,Point D)
{
    Vector V1=B-A;
    Vector V2=D-C;
    Vector V3=C-A;
    double t=Cross(V3,V2)/Cross(V1,V2);
    return A+V1*t;
}

bool OnSegment(Point P,Point A,Point B)
{
    Vector V1=A-P;
    Vector V2=B-P;
    return dcmp(Len(V1))==0||dcmp(Len(V2))==0||(dcmp(Cross(V1,V2))==0&&dcmp(Dot(V1,V2))<0);
}

bool SegmentIntersection(Point A,Point B,Point C,Point D)
{
    if(dcmp(Cross(B-A,D-C))==0) return false;
    if(OnSegment(A,C,D)||OnSegment(B,C,D)||OnSegment(C,A,B)||OnSegment(D,A,B)) return true;
    Vector V1=C-A;
    Vector V2=D-A;
    Vector V3=A-C;
    Vector V4=B-C;
    return dcmp(Cross(B-A,V1))*dcmp(Cross(B-A,V2))<0&&dcmp(Cross(D-C,V3))*dcmp(Cross(D-C,V4))<0;
}

const int maxn = 110;
int n;
Point P[maxn];
Point Poly[maxn*maxn];
int cnt;
Point s;
Point last,now;

bool cmp1(Point A,Point B)
{
    return dcmp(Dist(last,A)-Dist(last,B))>0;
}
bool cmp2(Point A,Point B)
{
    Vector V1=A-now;
    Vector V2=B-now;
    Vector V3=now-last;
    if(dcmp(Cross(V3,V1))*dcmp(Cross(V3,V2))<0) return dcmp(Cross(V3,V2))<0;
    else if(dcmp(Cross(V3,V2))==0&&dcmp(Dot(V3,V2))<0) return false;
    else if(dcmp(Cross(V3,V1))==0&&dcmp(Dot(V3,V1))<0) return true;
    return dcmp(Cross(V1,V2))<=0;
}

int main()
{
    while(~scanf("%d",&n))
    {
        s=Point(200,200);
        for(int i=0;i<n;i++)
        {
            P[i].Read();
            if(P[i]<s) s=P[i];
        }
        last=s;
        last.x-=10;
        now=s;
        Point Next;
        cnt=0;
        while(1)
        {
            Poly[cnt++]=now;
            bool you=false;
            for(int i=0;i<n;i++)
                if(OnSegment(now,P[i],P[(i+1)%n]))
                {
                    if(!OnSegment(P[i],now,last)&&(!you||cmp2(Next,P[i]))) Next=P[i],you=true;
                    if(!OnSegment(P[(i+1)%n],now,last)&&(!you||cmp2(Next,P[(i+1)%n]))) Next=P[(i+1)%n],you=true;
                }
            for(int i=0;i<n;i++)
                if(SegmentIntersection(now,Next,P[i],P[(i+1)%n]))
                {
                    Point Temp=GetIntersection(now,Next,P[i],P[(i+1)%n]);
                    if(Temp!=now) Next=Temp;
                }
            if(Next==s) break;
            last=now;
            now=Next;
        }
        printf("%d\n",cnt);
        for(int i=0;i<cnt;i++)
            printf("%lf %lf\n",Poly[i].x,Poly[i].y);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值