看着题标我就知道肯定要被坑,诶学长推荐的十道几何入门题。。怎么越来越坑了,题目我都没看明白,后面才知道原来求两线段构成个V来接水,但情况好像有点多啊,好多接不到水啊的,还要求交点啊,都不会啊,弱菜只能看着别人的代码,学习怎么去解题,代码好长的感觉,有的甚至上两百行了,要不要这么残暴啊,选了好几份看了思路又学习别人的各种模版巧了代码还是一直WA····,各种坐标x和y弄混,+写成-太是考验了,诶最后硬是撑了一天半整整才算明白。
思路:
1.求两线段构成的槽能装多少雨水,其实就是求相交后那个凹槽面积,不过这里有很多情况。(注:雨水垂直落地的)
情况:1.不相交就肯定不能接水了, 2.相交的话有会有在一条直线上重合的情况这种也不行 3.相交交点刚好为线段端点也不行 4相交后上线段完全遮挡了下线段也不行
这些都是接不到水的情况
剩下的就是可以接水的了,首先求出交点,然后以两线段中那个向上的Y坐标最小的作为一个端点,在用横线相交求出另外一个交点,最后根据x乘求面积。
这个题细心的地方有很多,而且也包含很多几何常用知识,很好的一个题,收获很大。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
const double eps=1e-8;
struct point{
double x;
double y;
}s,d1,d2;;//s交点,d1,d2为由s为端点的剩余三角形的两个点
struct line{
point a;
point b;
}line1,line2;
int dblcmp(double x){//由于精度问题,这里是一个比较模版
if(fabs(x)<eps)
return 0;
return x>0.0?1:-1;
}
double xmult(point p0,point p1,point p2){ //叉乘
return (p1.x-p0.x)*(p2.y-p0.y)-(p1.y-p0.y)*(p2.x-p0.x);
}
double dotmult(point p0,point p1,point p2){//点乘,可以很好的判断一个在直线上的点,是否已然在这个直线上的一个线段上
return (p1.x-p0.x)*(p2.x-p0.x)+(p1.y-p0.y)*(p2.y-p0.y);
}
int judgecross(line L1,line L2){
double s1,s2,s3,s4;
s1=dblcmp(xmult(L1.a,L1.b,L2.a));
s2=dblcmp(xmult(L1.a,L1.b,L2.b));
s3=dblcmp(xmult(L2.a,L2.b,L1.a));
s4=dblcmp(xmult(L2.a,L2.b,L1.b));
if(s1*s2<0&&s3*s4<0)return 1;//常规相交
if(s1==0&&dblcmp(dotmult(L2.a,L1.a,L1.b))<=0)return 1;//非常规相交 这种情况就需要用点成来判断是否只是在直线上而非线段上
if(s2==0&&dblcmp(dotmult(L2.b,L1.a,L1.b))<=0)return 1;
if(s3==0&&dblcmp(dotmult(L1.a,L2.a,L2.b))<=0)return 1;
if(s4==0&&dblcmp(dotmult(L1.b,L2.a,L2.b))<=0)return 1;
return 0;
}
void getcross(line L1,line L2){//获得交点
s=L1.a;
double t=((L1.a.x-L2.a.x)*(L2.a.y-L2.b.y)-(L1.a.y-L2.a.y)*(L2.a.x-L2.b.x))//这是利用了面积比与线段比的关系由于底相同,就便是边的比
/((L1.a.x-L1.b.x)*(L2.a.y-L2.b.y)-(L1.a.y-L1.b.y)*(L2.a.x-L2.b.x));
s.x+=(L1.b.x-L1.a.x)*fabs(t);//fabs可以去掉,但我不知道为什么可以,这里有向面积符号没有影响么?
s.y+=(L1.b.y-L1.a.y)*fabs(t);
}
void getpoint(point p1, point p2, double yy){
double dx=p2.x-p1.x;
double dy=p2.y-p1.y;
d1.y=yy;
if(dblcmp(dx)==0)
d1.x=p1.x;
else{
double k=dy/dx;
d1.x=(yy-p1.y)/k+p1.x;
}
}
double solve(line L1,line L2){
line L3;//作为一个替代板,和L2的b点相交并且平行于y轴;
if(dblcmp((L1.b.x-L1.a.x)*(L2.b.y-L2.a.y)-(L2.b.x-L2.a.x)*(L1.b.y-L1.a.y))==0)return 0.00;//两条线段重合了
L3.a.x=L3.b.x=L2.b.x; L3.a.y=L2.b.y; L3.b.y=L2.b.y+10000.0;
if(judgecross(L1,L3))return 0.00;//被上板覆盖接不到雨水
getcross(L1,L2);//获得交点
if(!dblcmp(s.y-L1.b.y)||!dblcmp(s.y-L2.b.y))return 0.00;//交点刚好是线段端点
getpoint(L1.a,L1.b,L2.b.y);//获得实际高边的三角形端点
d2=L2.b;//因为这是比较矮的那条边已经是三角形的一个顶点
return fabs(xmult(s,d1,d2))/2.0;
}
void swp(point &p1,point &p2){
point p;
p.x=p1.x, p.y=p1.y; p1.x=p2.x, p1.y=p2.y; p2.x=p.x, p2.y=p.y;
}
int main(){
int t;
double ans;
scanf("%d",&t);
while(t--){
scanf("%lf %lf %lf %lf",&line1.a.x,&line1.a.y,&line1.b.x,&line1.b.y);
scanf("%lf %lf %lf %lf",&line2.a.x,&line2.a.y,&line2.b.x,&line2.b.y);
if(dblcmp(line1.a.y-line1.b.y)>0)//先把这些点处理一下方便用,这里将每条线段的两个点按照纵坐标从小到大排好
swp(line1.a,line1.b);
if(dblcmp(line2.a.y-line2.b.y)>0)
swp(line2.a,line2.b);
if(dblcmp(line1.b.y-line2.b.y)<0){//这里同样是为了方便后面处理,将y坐标比较高的那条线段列为第一条线段
swp(line1.a,line2.a);
swp(line1.b,line2.b);
}
if(!dblcmp(line1.a.y-line1.b.y)||!dblcmp(line2.a.y-line2.b.y))ans=0.00;//如果有其中一条板是平行的,答案就是0;
else{//不平行那么就属于其他的情况
if(judgecross(line1,line2))ans=solve(line1,line2);
else ans=0.00;
}
printf("%.2lf\n",ans);
}
return 0;
}