poj 2826

来源:互联网 发布:软件测试需要会什么 编辑:程序博客网 时间:2024/05/17 22:15

题目概述

在二维平面中,将两块直木板的某个位置钉起来装雨水,雨水从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-6const 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

0 0
原创粉丝点击