51nod-1369 无穷印章

(一)题面:

51nod-1369 无穷印章

有一个印章,其完全由线段构成。这些线段的线足够细可以忽略其宽度,就像数学上对线的定义一样,它们没有面积。现在给你一张巨大的白纸(10亿x10亿大小的纸,虽然这个纸很大,但是它的面积毕竟还是有限的),你可以在上面盖这个印章。要求盖印章时只能平移印章不能将其旋转,同时两次印章盖下的痕迹不能有交点(存在交点,指的是两次盖完印章后,可以从第一次的印章图案中取出一条线段,同时第二次的图案中也取出一条线段,且这两天线段相交)。给定印章中的图案,请判断是否有办法在这个有限的白纸上盖无限个章?存在输出"Infinite",否则输出"Finite".

提示:下图给出一些印章图案以及它们对应的结果。

Input

多组测试数据,第一行一个整数T,表示测试数据数量,1<=T<=5
每组测试数据有相同的结构构成。
每组数据的第一行包含一个整数N,表示印章中的线段条数,其中1<=N<=50
接下来N行每行四个整数ai,bi,ci,di,表示一条线段的两个端点(ai,bi)与(ci,di),其中-1000<=ai,bi,ci,di<=1000

Output

每组数据一行输出,存在无穷个印章的盖法输出"Infinite",否则输出"Finite".

Input示例

3
1
0 0 0 1
4
0 0 1 0
1 0 1 1
1 1 0 1
0 1 0 0
7
0 0 1 3
1 3 2 0
2 0 3 3
3 3 4 0
-1 3 -3 2
-3 2 -1 1
-1 1 -3 0

Output示例

Infinite
Finite
Finite

(二)题意:

(中文题详见题面)

 

(三)题解:

首先:章可以印无数次,当且仅当至少存在这么一个方向:朝这个方向进行一个微小(趋近于零)的位移以后,再次印的章与前一次不相交(证明略...)。

故我们由此可以得到一个思路:枚举位移的方向,然后进行一个极小的位移,看位移前后是否相交。然而这样显然会有一些方向枚举不到。实际上我这么写完以后对于某些数据要么WA(枚举间隔太大)要么TLE(枚举间隔太小)。

我们进一步分析:

①对于单一的线段,显然可以满足条件(只有线段的方向不满足位移条件);

②对于两条线段,若两条线段没有交点,那么这两条线段也能满足条件(两条线段的方向不满足条件);

③若两条线段有两个端点重合,那么这两条线段可以确定两个满足位移的方向的范围(如下):

如图中的线段AB与AC及其延长线形成了四个角度α、β、γ、θ,显然地,α、β范围内的方向为可行方向,γ、θ为不可行方向。

④对于多个图(三)中的情况,我们可以得到多组不可行方向大的范围,如果最终这个范围覆盖了360°,那么最终结果就是“Finite”,否则是“Infinite”。

⑤同上分析,当存在两条线段满足下面两种情况之一时,必然不能印无穷次:

 

 

(四)代码:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
#define esp 1e-10
#define PI acos(-1)
using namespace std;
struct Point{
    double x,y;
    Point(double _x=0,double _y=0):x(_x),y(_y){}
    void input(){scanf("%lf%lf",&x,&y);}
};
typedef Point Vector;
Vector operator - (Vector A,Vector B){
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator * (Vector A,double d){
    return Vector(A.x*d,A.y*d);
}
int dcmp(double x){
    if(fabs(x)<esp)return 0;
    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 Dot(Vector A,Vector B){        //点乘
    return (A.x*B.x+A.y*B.y);
}
double angle(Vector v){               //向量的极角:从x轴正方向转到该向量的弧度,逆时针-正,顺时针-负
    return atan2(v.y,v.x);
}
double Cross(Vector A,Vector B){      //叉乘
    return (A.x*B.y-A.y*B.x);
}
bool OnLine(Point P,Point A,Point B){               //点在直线上
    return dcmp(Cross(P-A,P-B))==0;
}
bool OnSegment(Point P,Point A,Point B){            //判断点在线段上--不包括恰好在端点重合
    return dcmp(Cross(P-A,P-B))==0&&dcmp(Dot(P-A,P-B))<0;
}
//判断线段规范相交->即线段的两个端点分别在另外一条线段的两侧->叉乘异号(不包括恰好在端点重合)
bool SegmentProperIntersection(Point A1,Point A2,Point B1,Point B2){
    double c1=Cross(A2-A1,B1-A1),c2=Cross(A2-A1,B2-A1),
           c3=Cross(B2-B1,A1-B1),c4=Cross(B2-B1,A2-B1);
    return  (dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0);
}
bool MysegmentProperIntersection(Point A1,Point A2,Point B1,Point B2){
    return OnSegment(A1,B1,B2)||OnSegment(A2,B1,B2)||
           OnSegment(B1,A1,A2)||OnSegment(B2,A1,A2)||
           SegmentProperIntersection(A1,A2,B1,B2);
}
struct Line{
    Point A,B;
    Line(Point _A=Point(0,0),Point _B=Point(0,0)){
        A=_A;B=_B;
    }
    void input(){A.input();B.input();}
}line[55];
struct Interval{
    double l,r;
    Interval(double _l=0,double _r=0){
        l=_l;r=_r;
    }
    bool operator < (const Interval &i)const{
        return l<i.l||(l==i.l&&r>i.r);
    }
};
vector<Interval>interval;
int main(){
    freopen("in.txt","r",stdin);
    int T,n;scanf("%d",&T);
    while(T--){
        bool ok=1;
        scanf("%d",&n);interval.clear();
        for(int i=0;i<n;i++)line[i].input();
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                if(dcmp(Cross(line[i].A-line[i].B,line[j].A-line[j].B)==0))continue;
                if(MysegmentProperIntersection(line[i].A,line[i].B,line[j].A,line[j].B)){
                    ok=0;break;
                }
                if(line[i].A==line[j].B)swap(line[j].A,line[j].B);
                else if(line[i].B==line[j].A)swap(line[i].A,line[i].B);
                else if(line[i].B==line[j].B){
                    swap(line[i].A,line[i].B);swap(line[j].A,line[j].B);
                }
                if(line[i].A==line[j].A){
                    double rad1=angle(line[i].B-line[i].A);
                    double rad2=angle(line[j].B-line[j].A);
                    double rad3=rad1+PI,rad4=rad2+PI;
                    double l1,r1,l2,r2;
                    if(rad1<0)rad1+=2*PI;
                    if(rad2<0)rad2+=2*PI;
                    if(rad1>PI&&rad2<=PI)swap(rad1,rad2);
                    else if(rad1<=PI&&rad1<=PI&&rad1>rad2)swap(rad1,rad2);
                    else if(rad1>PI&&rad1>PI&&rad1<rad2)swap(rad1,rad2);
                    else if(rad1<=PI&&rad2>PI)swap(rad1,rad2);
                    if(rad1<=PI){
                        if(rad1+PI<=rad2){
                            l1=rad1;r1=rad2-PI;
                            l2=rad1+PI;r2=rad2;
                        }
                        else{
                            l1=rad2+PI;r1=rad1+2*PI;
                            l2=rad2;r2=rad1+PI;
                        }
                    }
                    else{
                        if(rad1-PI<=rad2){
                            l1=rad1;r1=rad2+PI;
                            l2=rad1-PI;r2=rad2;
                        }
                        else{
                            l1=rad2+PI;r1=rad1;
                            l2=rad2;r2=rad1-PI;
                        }
                    }
                    interval.push_back(Interval(l1,r1));
                    interval.push_back(Interval(l2,r2));
                }
            }
        }
        if(ok&&interval.size()>0){
            sort(interval.begin(),interval.end());
            double L=interval[0].l,R=interval[0].r,sum=R-L;
            for(int i=1;i<interval.size();i++){
                if(interval[i].l>R)break;
                else if(interval[i].r>R){
                    sum+=interval[i].r-R;
                    R=interval[i].r;
                }
                if(sum>=2*PI){ok=0;break;}
            }
        }
        printf(ok?"Infinite\n":"Finite\n");
    }
    return 0;
}

 

(五)总结:

这个题首先枚举方向,然后wa--tle--wa...

然后看了题解以后就wa-wa-wa...

主要是没有理清思路再动笔,然后就一直改不对QWQ,其实那个角度的计算画个图就比较清楚了的说。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值