直线交点,欧拉定理,计算几何(好看的一笔画,LA 3263)

自己写得手忙脚乱的,而标程好简洁。

我发现这种情况有一个特点,那就是我总想着一次性搞定一些东西,而不是分两次。比如此题,我总想着一次性求出e与v,而不是先求出v再慢慢求e。

可能是因为做算法题做习惯了,总是追求高效率,但是自己追求高效率的方法是不对的,总是希望一次性求出所有东西。事实上这样效率不但没有显著提高,还会把程序结构弄得乱七八糟,从而导致出现各种奇奇怪怪的bug,然后也很难看清思路与检查。其实说了那么多都没说到本质,只不过是因为自己不会做或者对这类题型不够熟练,所以才会走上弯路,如果自己完全会做或者非常熟悉这类问题,便不会出现这种糟糕的情况了。还要多加练习。


分析

很容易想到要用平面图的欧拉定理,所以要想办法求v和e。

v其实挺好求的,就是枚举所有线段,如果相交就求出交点,然后放进set里就好了。但是会有精度问题,需要用到eps并重载==解决,而set只能重载<而不能重载==,所以要另谋出路。其实出路很多,可以像书上那样重载==和<后用sort+unique大法(这简直就是一个set呀)。也可以写一写查找表来搞(顺序表估计会超时啦)。但是该如何求e呢,我就总想着在求v的时候顺便求出e,然后也是没想清楚就开始写代码(大忌啊,未证明正确性就开始写代码==浪费时间),然后就各种错啊,然后就不会啦。事实上的话很难在点和边上找到比较明确且简洁的关系啦,这个点是端点相交啊还是规范相交啊,好多个边都交在一个点上怎么办啊,其中有些又是规范相交有些又是端点相交,端点相交还要细分呢。。怎么搞得过来呀。事实上有更霸气的解决方法啦,你不是都把所有点都找出来了吗,那就枚举所有点和边,单纯的考虑一个点和一个边的话,是不是就很好判断边是如何被点分隔的了呢?所以枚举一遍统计一下就好了,至少思路上轻松太多,而且时间复杂度也能承受呀,就O(n^2)*O(n)=O(n^3)完全可以接受的。一看这道题的数据范围就能感觉到是O(n^3)的题啦。如果只用到O(n^2)的算法不觉得奇怪吗。


代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 300 + 10;
struct Point
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y){}
};

typedef Point Vector;

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

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

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

bool operator < (Point A,Point B)
{
    return A.x<B.x||(A.x==B.x&&A.y<B.y);
}

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

Point GetInterSection(Point P,Vector v,Point Q,Vector w)
{
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}

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

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

bool InterSection(Point a1,Point a2,Point b1,Point b2)
{
    double c1=Cross(a2-a1,b1-a1);
    double c2=Cross(a2-a1,b2-a1);
    double c3=Cross(b2-b1,a1-b1);
    double c4=Cross(b2-b1,a2-b1);
    return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}

bool InLine(Point P,Point A,Point B)
{
    Vector v1=A-P;
    Vector v2=B-P;
    return dcmp(Cross(v1,v2))==0&&dcmp(Dot(v1,v2))<0;
}

Point Read()
{
    double x,y;
    scanf("%lf %lf",&x,&y);
    return Point(x,y);
}

int N,e,v;
Point a[maxn];
Point V[maxn*maxn];
int kase=0;

int main()
{
    while(scanf("%d",&N)==1&&N)
    {
        for(int i=0;i<N;i++)
        {
            a[i]=Read();
            V[i]=a[i];
        }
        N--;
        e=v=N;
        for(int i=0;i<N;i++)
            for(int j=0;j<i;j++)
                if(InterSection(a[i],a[i+1],a[j],a[j+1]))
                    V[v++]=GetInterSection(a[i],a[i+1]-a[i],a[j],a[j+1]-a[j]);
        sort(V,V+v);
        v=unique(V,V+v)-V;
        for(int i=0;i<v;i++)
            for(int j=0;j<N;j++)
                if(InLine(V[i],a[j],a[j+1]))
                    e++;
        printf("Case %d: There are %d pieces.\n",++kase,e-v+2);
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值