poj-2826 An Easy Problem?!(计算几何,好题)

题目链接:点击打开链接

An Easy Problem?!
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 11568 Accepted: 1759

Description

It's raining outside. Farmer Johnson's bull Ben wants some rain to water his flowers. Ben nails two wooden boards on the wall of his barn. Shown in the pictures below, the two boards on the wall just look like two segments on the plane, as they have the same width. 

Your mission is to calculate how much rain these two boards can collect. 

Input

The first line contains the number of test cases. 
Each test case consists of 8 integers not exceeding 10,000 by absolute value,  x 1y 1x 2y 2x 3y 3x 4y 4. ( x 1y 1), ( x 2y 2) are the endpoints of one board, and ( x 3y 3), ( x 4y 4) are the endpoints of the other one.

Output

For each test case output a single line containing a real number with precision up to two decimal places - the amount of rain collected. 

Sample Input

2
0 1 1 0
1 0 2 1

0 1 2 1
1 0 1 2

Sample Output

1.00
0.00
题意:两根木棍,摆放的方式由题目给出,问接到雨水的量是多少。(注意是平面,只要考虑面积)

思路:题目看似很简单,实际上却相当的恶心啊....说来惭愧,差点就1A了,就因为斜率偷懒了没用公式结果白WA了一个小时。

具体看代码吧,打了注释。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define eps 1e-8
struct Node
{
    double x,y;
};
double mulit(Node a,Node c,Node b)//向量ac与向量ab叉乘
{
    return (c.x-a.x)*(b.y-a.y)-(c.y-a.y)*(b.x-a.x);
}
Node get_point(Node a,Node b,Node c,Node d)//求直线ab与直线cd的交点(已证明线段与线段相交,则可以当做两直线)
{
    Node ret=a;
    double t=((a.x-c.x)*(c.y-d.y)-(a.y-c.y)*(c.x-d.x))
             /((a.x-b.x)*(c.y-d.y)-(a.y-b.y)*(c.x-d.x));
    ret.x+=(b.x-a.x)*t;
    ret.y+=(b.y-a.y)*t;
    return ret;
}
int check(Node a,Node b,Node c,Node d)
{
    if(mulit(a,c,b)*mulit(a,d,b)<eps&&mulit(c,a,d)*mulit(c,b,d)<eps)//注意这里必须要小于eps而不能<=0,不然会因为精度问题而出错
        return 1;
    return 0;
}
int main()
{
    int T;
    Node a,b,c,d;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lf %lf %lf %lf",&a.x,&a.y,&b.x,&b.y);
        scanf("%lf %lf %lf %lf",&c.x,&c.y,&d.x,&d.y);
        if(a.y==b.y||c.y==d.y)//有一根木棍是水平的,那么无论另外一根如何放置都不可能接到水了
        {
            printf("0.00\n");
            continue;
        }
        if(check(a,b,c,d))//检查线段是否相交
        {
            Node point=get_point(a,b,c,d);//获得交点
            Node r[10];
            int top=0;
            if(a.y>point.y)
                r[top++]=a;
            if(b.y>point.y)
                r[top++]=b;
            if(c.y>point.y)
                r[top++]=c;
            if(d.y>point.y)
                r[top++]=d;//将在交点上面的点保存,因为交点下面的点无意义(该点与交点会形成一个斜向右下角或者左下角的板子,无法保存水)
            double sum;
            for(int j=0; j<top; j++)//遍历所有可行点
            {
                for(int k=0; k<top; k++)//找到一个点在j点的左边且在j点的逆时针方向。左边是因为在j点右边或者和j点x坐标相等的点是无意义的,水从高处落下时会被上面的板子挡掉
                    if(r[k].x<r[j].x&&mulit(point,r[j],r[k])>0)//在j点的逆时针方向是因为如果找到的第二个板子在j点的顺时针方向,那同样会被j板子挡掉,无法保存
                    {
                        Node p=r[k],q=r[j];
                        if(p.y<q.y)//p板子在q板子下面,则要在q和交点形成的直线上取一个高为p.y的点,因为只有等高才能装水
                        {
                            if(q.x!=point.x)//如果q板子是垂直的话,x坐标不变
                            {
                                double kk=(q.y-point.y)/(q.x-point.x);//直线q-point斜率
                                double l=point.y-kk*point.x;//y=ax+b的b
                                q.x=(p.y-l)/kk;//知道y,求出x。x=(y-b)/k
                            }
                            q.y=p.y;
                        }
                        else if(p.y>q.y)
                        {
                            if(p.x!=point.x)
                            {
                                double kk=(p.y-point.y)/(p.x-point.x);
                                double l=point.y-kk*point.x;
                                p.x=(q.y-l)/kk;
                            }
                            p.y=q.y;
                        }
                        sum=mulit(point,q,p)/2;//接到水的区域一定是个三角形,因为是两个板子组成的
                    }
            }
            printf("%.2lf\n",sum);
        }
        else//线段不相交则无法接到水
            printf("0.00\n");
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值