[题目要求]
http://acm.bupt.edu.cn/onlinejudge/newoj/showProblem/show_problem.php?problem_id=18
http://poj.org/problem?id=1410
[题目涉及的相关理论与算法]
叉积判断一个点与另一个线段的关系
[题目中需要注意的地方]
这个题其实问题也挺多的,问题是对单词intersection的理解,应该是取相交的意思。我看到有人评论说线段在里面也算,认为题目没说清楚。。其实人家题目中有原话“The rectangle consists of four straight lines and the area in between.“说的很清楚,矩形包括里面的area.显然全在里面是可以的。
另外一个问题是他说的矩形的输入”左上“和”右下“,不保证左比右小,上比下小,所以进入的数据要判断下。这个题目挺水的...像我这种新手第一次提交就PE了,PE基本上和AC差不多哈,然后加了个换行就AC了,我把结果放到POJ中却是WA,原因就在这第二个问题上了。好像北邮的测试数据是保证左上右下的吧。。具体的我也不清楚。
[思路过程]
考察一个线段与矩形的关系。我先想肯定不相交的情况,可以把这个线段设想成一个边与坐标轴平行的新矩形的对角线,即使线段本身和坐标轴平行,也可以理解成没有面积的矩形。然后对目标矩形四周的八个方向划分,发现只有新矩形和目标矩形出现相交,才有可能对角线线段与目标矩形相交。这种情况的线段必然不符合提议。
第一步筛选后,再筛选出整个新矩形均在目标矩形内部的,这里面的线段是必然符合题意的。
排除上面的情况后,我们就开始与目标矩形边相交的情况。我当时是用的线段的点斜式带入的目标矩形的四个点,然后分别讨论,如果对应的两个值在矩形边的两侧,则说明相交,否则就不相交(这个不好说清楚,但是很容易理解)。
我在这里其实没讨论第三步中点斜式是否存在的情况,即有一个斜率不存在的可能,加上就可以了。加上之前北邮的系统就AC了,但是北大的WA,后来找了测试数据对比发现的。。所以说,有时就是AC的程序里面很可能还有问题的= =
还有一个是我试着写了一个类来讨论对线段做处理,因为后面要用到点斜式方程求解。。所以觉得这样写方便,不过我在网上极少看到用类做acm。。是不是因为速度的问题呢?北邮系统我的时间非常慢。。不知道为啥,在北大就是正常的0ms..还有北大里面把G++和C++区分开来了,我用C++是0ms,而G++是16ms。。真的好蛋疼=。=
[代码]
#include<iostream>
using namespace std;
const int MAX=40000; //这个。。。就是随便给斜率一个初始值。。
typedef struct Node{
double x;
double y;
}Node; //储存节点的坐标结构体。
class Line{ //建立一个线段类。
private:
double k;
Node Lsp,Lep;
public:
double small,big,top,base;//这里四个数据存储的是这条线段的四个边界
//(就是前面说的把线段设想成一个新矩形的对角线)然后这四个就是他们的边界。
Line(Node sp,Node ep) //构造函数,下面的比较过程就是把边界取出来
{
Lsp=sp;
Lep=ep; //进来的节点也储存一下。为后面的求值使用。
k=MAX;
if(sp.x < ep.x)
{
small=sp.x;
big=ep.x;
}
else
{
small=ep.x;
big=sp.x;
}
if(sp.y < ep.y)
{
base=sp.y;
top=ep.y;
}
else
{
base=ep.y;
top=sp.y;
}
}
bool isK() //判断是否存在斜率。
{
if(Lsp.x == Lep.x) return false;
else
{
k =(Lsp.y-Lep.y)/(Lsp.x-Lep.x);
return true;
}
}
double forY(double x)
{
return k*(x-Lsp.x)+Lsp.y; //从横坐标求线段延长线的纵坐标。
}
double forX(double y)
{
return (y-Lsp.y)/k+Lsp.x; //从坐标求横坐标
}
};
bool caseSolve()
{
Node sp,ep;
cin>>sp.x>>sp.y>>ep.x>>ep.y;
Line line = Line(sp,ep);
double left,top,right,bottom; //这里储存输入的矩形两个顶点注意,有可能不是左上和右下。
cin>>left>>top>>right>>bottom; //把可能不是左上和右下,变成真正的左上和右下。
if(left>right){double temp = left;left = right;right = temp;}
if(bottom>top){double temp = bottom;bottom = top; top = temp;}
if(line.base > top || line.top < bottom || line.small > right || line.big < left)
return false; //这个是第一种排除。四周的区域。
if(line.top <= top && line.base >= bottom && line.big <= right && line.small >= left)
return true; //这个是接着把在矩形里面的线段筛选出来。
double num1=0,num2=0,num3=0,num4=0;
if(!line.isK()) //经过前面的两次筛选,再考虑斜率的问题.注意line.isK()已经初始化k了。
{ //这是不存在的情况。
if(line.big <= right && line.big >= left) return true;//在矩形的左右之间。则是可以的。
else return false;
};
num1 = line.forY(left);
num2 = line.forY(right);
num3 = line.forX(top);
num4 = line.forX(bottom);//斜着的情况。这时判断矩形两边的端点在线段上的对应位置是否在矩形边两侧。
if((num1-top)*(num2-top) < 0) return true;
else if((num1-bottom)*(num2-bottom) < 0) return true;
else if((num3-right)*(num4-right) < 0) return true;
else if((num3-left)*(num4-left) < 0) return true;
else return false;
}
int main()
{
//freopen("in.txt", "r", stdin);
//freopen("out.txt", "w", stdout);
int t;
cin>>t;
while((t--)!=0)
{
if(caseSolve()) cout<<"T"<<endl;
else cout<<"F"<<endl;
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
[一点点小积累]
1. 使用叉积在给定三个点的坐标下求出某一个点和另两个点构成的线段的位置关系。
int cross(point_Ta, point_T b, point_T c){//叉积,结果大于0则AB在AC的顺时针方向上,小于0在逆时针方向,等于0则共线
int x1 = b.x -a.x;
int y1 = b.y -a.y;
int x2 = c.x -a.x;
int y2 = c.y -a.y;
return (x1 * y2- x2 * y1);
}
将这个式子展开,其实就是一个点到另两个点的斜率做除法。与1比较。但是写成叉积的形式可以包含斜率不存在的情况。
[尾声]
其实是一个水题...但是做题就应该是这样,总不能一直是一帆风顺,也总不能一直是坎坷崎岖,保证好一个正常心态吧~今天的歌曲是梁静茹的爱久见人心~ 什么说到底都是坚持!