(一)题面:
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,其实那个角度的计算画个图就比较清楚了的说。