poj 2826

题目概述

在二维平面中,将两块直木板的某个位置钉起来装雨水,雨水从y轴正方向向下落下,给出木板四个端点的坐标,且木板可视为线段,问钉成的木板装满水时雨水的横截面积

时限

1000ms/3000ms

输入

第一行正整数N,代表数据组数,第二行四个整数,描述第一块木板端点坐标,第三行四个整数,描述第二块木板端点坐标,两组中间会有一个空行

限制

坐标绝对值<=10000

输出

每行一个数,所求横截面积,保留2位小数

样例输入

2
0 1 1 0
1 0 2 1

0 1 2 1
1 0 1 2

样例输出

1.00
0.00

讨论

计算几何,非常复杂,需要充分讨论所有可能的情况,虽然输入都是整数,但是最好直接当浮点数处理,整体思路是先确定相交情况,然后排除掉横截面积必然为0的情况,比如角度非正,线段在同一象限但是上面的完全挡住下面的,之后确定交点,确定能接水的三角形的边的端点位置,然后求出面积,具体都会体现在代码中,这里不过多解释
代码看着长,实际上冗余度很大,但是实在懒得修改了

题解状态

220K,16MS,C++,6837B

题解代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f  
#define MAXN 53
#define memset0(a) memset(a,0,sizeof(a))
#define EPS 1e-6

const double pi = atan(1.0) * 4;//pi值 留作备用
double dp(double x1, double y1, double x2, double y2, double x3, double y3)//dot_product 数量积 以第二个点为公共起点
{
    return (x1 - x2)*(x3 - x2) + (y1 - y2)*(y3 - y2);
}
double xp(double x1, double y1, double x2, double y2, double x3, double y3)//cross_product 向量积 以第二个点为公共起点
{
    return (x1 - x2)*(y3 - y2) - (y1 - y2)*(x3 - x2);
}
double projx(double x1, double y1, double x2, double y2)//projection_on_x_axis 在x轴射影 以第一个点为起点
{
    return abs(dp(x2, y2, x1, y1, x1 + 1, y1));
}
double projy(double x1, double y1, double x2, double y2)//projection_on_y_axis 在y轴射影 以第一个点为起点
{
    return abs(dp(x2, y2, x1, y1, x1, y1 + 1));
}
int quadrant(double a)//象限 参数是角度 在轴上会返回0
{
    if (0 < a&&a < 90)
        return 1;
    if (90 < a&&a < 180)
        return 2;
    if (-180 < a&&a < -90)
        return 3;
    if (-90 < a&&a < 0)
        return 4;
    else
        return 0;
}
bool onsegment(double x, double y, double x1, double y1, double x2, double y2)//快速排斥实验 以后两个点构成矩形
{
    return min(x1, x2) <= x&&x <= max(x1, x2) && min(y1, y2) <= y&&y <= max(y1, y2);
}
int intersect(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)//相交 输入是所有四个点的坐标
{
    double xp1 = xp(x3, y3, x1, y1, x2, y2);
    double xp2 = xp(x4, y4, x1, y1, x2, y2);
    double xp3 = xp(x1, y1, x3, y3, x4, y4);
    double xp4 = xp(x2, y2, x3, y3, x4, y4);
    if (xp1*xp2 < 0 && xp3*xp4 < 0)
        return 5;//最普通的X形相交 返回5
    int ans = 0;
    if (abs(xp1) < EPS&&onsegment(x3, y3, x1, y1, x2, y2))
        ans++;//T型相交 有1个点在另一条线段上 返回1
    if (abs(xp2) < EPS&&onsegment(x4, y4, x1, y1, x2, y2))
        ans++;//V型相交 有2个点在另一条线段上 返回2
    if (abs(xp3) < EPS&&onsegment(x1, y1, x3, y3, x4, y4))
        ans++;//不完全重合 两条线段有一端重合 另一端共线 有3个点在另一条线段上 返回3
    if (abs(xp4) < EPS&&onsegment(x2, y2, x3, y3, x4, y4))
        ans++;//完全重合 返回4
    return ans;//如果不相交 则返回0
}
double dis(double x1, double y1, double x2, double y2)//distance 两点间距离
{
    return sqrt((x2 - x1)*(x2 - x1) + (y2 - y1)*(y2 - y1));
}
double angle(double x1, double y1, double x2, double y2)//角度 利用数量积求出余弦值 进而求出角度 顺便判断正负
{
    double a = acos(dp(x2, y2, x1, y1, x1 + 1, y1) / dis(x1, y1, x2, y2)) * 180 / pi;
    return y2 > y1 ? a : -a;
}
double area(double x1, double y1, double x, double y, double x2, double y2)//面积 用底乘高除二算
{
    return abs(x1 - x2)*projy(x, y, x2, y2) / 2;
}
double fun(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
{
    int intersection = intersect(x1, y1, x2, y2, x3, y3, x4, y4);//判断相交情况
    if (!intersection) {
        return 0;//不相交返回0
    }
    else if (intersection == 1) {//T型相交
        double poix, poiy, ox1, oy1, ox2, oy2;//point_of_intersection_x point_of_intersection_y 交点坐标 后面四个是交点上方的两个点的坐标 o只是为了区别一下而已
        double xp1 = xp(x3, y3, x1, y1, x2, y2);
        double xp2 = xp(x4, y4, x1, y1, x2, y2);
        double xp3 = xp(x1, y1, x3, y3, x4, y4);
        double xp4 = xp(x2, y2, x3, y3, x4, y4);//重新计算四个向量积 有点多余
        if (abs(xp1) < EPS) {
            poix = x3, poiy = y3;
            ox1 = x4, oy1 = y4;
            if (y1 > y2)
                oy2 = y1, ox2 = x1;
            else
                oy2 = y2, ox2 = x2;
        }
        else if (abs(xp2) < EPS) {
            poix = x4, poiy = y4;
            ox1 = x3, oy1 = y3;
            if (y1 > y2)
                oy2 = y1, ox2 = x1;
            else
                oy2 = y2, ox2 = x2;
        }
        else if (abs(xp3) < EPS) {
            poix = x1, poiy = y1;
            ox1 = x2, oy1 = y2;
            if (y3 > y4)
                oy2 = y3, ox2 = x3;
            else
                oy2 = y4, ox2 = x4;
        }
        else if (abs(xp4) < EPS) {//以唯一一个为0的向量积确定哪个点在另一条线段上 将这条线段上另一个点记为点o1 对于另一条线段 取y值较大者作为点o2
            poix = x2, poiy = y2;
            ox1 = x1, oy1 = y1;
            if (y3 > y4)
                oy2 = y3, ox2 = x3;
            else
                oy2 = y4, ox2 = x4;
        }
        if (oy1 < oy2) {//为方便处理 令点o1为y值较大者
            swap(ox1, ox2);
            swap(oy1, oy2);
        }
        double theta1 = angle(poix, poiy, ox1, oy1), theta2 = angle(poix, poiy, ox2, oy2);//求交点poi和点o1,o2的角度
        if (theta1 < EPS || theta2 < EPS)
            return 0;//如果有负的 肯定是0
        if (quadrant(theta1) == quadrant(theta2)) {//对于同象限需要进一步讨论
            if (((quadrant(theta1) == 1 && theta2<theta1) || (quadrant(theta1) == 2 && theta2>theta1)) && projx(poix, poiy, ox1, oy1) >= projx(poix, poiy, ox2, oy2))
                return 0;//如果同象限 并且在上方的一条在x轴投影较大 就会把下面的挡住
            else {
                double poi2y = oy2, poi2x = (poi2y - poiy) / (oy1 - poiy)*(ox1 - poix) + poix;//过较低的点o2做水平线交o1于poi2
                return area(poi2x, poi2y, poix, poiy, ox2, oy2);//求三角形poi,poi2,o2的面积
            }
        }
        else {
            double poi2y = oy2, poi2x = (poi2y - poiy) / (oy1 - poiy)*(ox1 - poix) + poix;
            return area(poi2x, poi2y, poix, poiy, ox2, oy2);//求面积
        }
    }
    else if (intersection == 2) {//V型相交
        double poix, poiy, ox1, oy1, ox2, oy2;
        if (abs(x1 - x3) < EPS&&abs(y1 - y3) < EPS) {
            poix = x1, poiy = y1;
            ox1 = x2, oy1 = y2, ox2 = x4, oy2 = y4;
        }
        else if (abs(x1 - x4) < EPS&&abs(y1 - y4) < EPS) {
            poix = x1, poiy = y1;
            ox1 = x2, oy1 = y2, ox2 = x3, oy2 = y3;
        }
        else if (abs(x2 - x3) < EPS&&abs(y2 - y3) < EPS) {
            poix = x2, poiy = y2;
            ox1 = x1, oy1 = y1, ox2 = x4, oy2 = y4;
        }
        else if (abs(x2 - x4) < EPS&&abs(y2 - y4) < EPS) {
            poix = x2, poiy = y2;
            ox1 = x1, oy1 = y1, ox2 = x3, oy2 = y3;
        }//差不多的原理找出交点poi和另外两个点o1,o2
        else {
            return 0;
        }//但会有些特殊情况 比如一条线段完全被包含在另一条线段里 交点也是2 但无一例外返回0
        if (oy1 < oy2) {//下面的处理就完全一样了
            swap(ox1, ox2);
            swap(oy1, oy2);
        }
        double theta1 = angle(poix, poiy, ox1, oy1), theta2 = angle(poix, poiy, ox2, oy2);
        if (theta1 < EPS || theta2 < EPS)
            return 0;
        if (quadrant(theta1) == quadrant(theta2)) {
            if (((quadrant(theta1) == 1 && theta2<theta1) || (quadrant(theta1) == 2 && theta2>theta1)) && projx(poix, poiy, ox1, oy1) >= projx(poix, poiy, ox2, oy2))
                return 0;
            else {
                double poi2y = oy2, poi2x = (poi2y - poiy) / (oy1 - poiy)*(ox1 - poix) + poix;
                return area(poi2x, poi2y, poix, poiy, ox2, oy2);
            }
        }
        else {
            double poi2y = oy2, poi2x = (poi2y - poiy) / (oy1 - poiy)*(ox1 - poix) + poix;
            return area(poi2x, poi2y, poix, poiy, ox2, oy2);
        }
    }
    else if (intersection == 5) {//X型相交
        double poix, poiy, ox1, oy1, ox2, oy2;
        if (abs(x1 - x2) < EPS) {
            double k3 = (y4 - y3) / (x4 - x3);
            poix = x1, poiy = k3*(x1 - x3) + y3;
        }
        else if (abs(x3 - x4) < EPS) {
            double k1 = (y2 - y1) / (x2 - x1);
            poix = x3, poiy = k1*(x3 - x1) + y1;
        }
        else {
            double k1 = (y2 - y1) / (x2 - x1);
            double k3 = (y4 - y3) / (x4 - x3);
            poix = (y3 - y1 + k1*x1 - k3*x3) / (k1 - k3), poiy = (k1*k3*(x3 - x1) + k3*y1 - k1*y3) / (k3 - k1);
        }//利用点斜式方程求出交点 从poj 1269搬来的部分源码
        if (y1 > y2)
            oy1 = y1, ox1 = x1;
        else
            oy1 = y2, ox1 = x2;
        if (y3 > y4)
            oy2 = y3, ox2 = x3;
        else
            oy2 = y4, ox2 = x4;
        if (oy1 < oy2) {//下面也是一样的
            swap(ox1, ox2);
            swap(oy1, oy2);
        }
        double theta1 = angle(poix, poiy, ox1, oy1), theta2 = angle(poix, poiy, ox2, oy2);
        if (theta1 < EPS || theta2 < EPS)
            return 0;
        if (quadrant(theta1) == quadrant(theta2)) {
            if (((quadrant(theta1) == 1 && theta2<theta1) || (quadrant(theta1) == 2 && theta2>theta1)) && projx(poix, poiy, ox1, oy1) >= projx(poix, poiy, ox2, oy2))
                return 0;
            else {
                double poi2y = oy2, poi2x = (poi2y - poiy) / (oy1 - poiy)*(ox1 - poix) + poix;
                return area(poi2x, poi2y, poix, poiy, ox2, oy2);
            }
        }
        else {
            double poi2y = oy2, poi2x = (poi2y - poiy) / (oy1 - poiy)*(ox1 - poix) + poix;
            return area(poi2x, poi2y, poix, poiy, ox2, oy2);
        }
    }
    else {
        return 0;//对于各种重合 统统返回0
    }
}
int main(void)
{
    //freopen("vs_cin.txt", "r", stdin);
    //freopen("vs_cout.txt", "w", stdout);

    int times;
    scanf("%d", &times);//input
    while (times--) {
        double x1, y1, x2, y2, x3, y3, x4, y4;
        scanf("%lf%lf%lf%lf%lf%lf%lf%lf", &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);//input
        printf("%.2lf\n", fun(x1, y1, x2, y2, x3, y3, x4, y4));//output
    }
}

EOF

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值