UVALive 4127

Link:click here
The question:给出n座山的高和宽,山视为等腰三角形,从上往下看,求山的轮廓的长度,山之间的空白不算。
Solution:用竖线在山与山的交点和顶点处,把所有山分成一段一段的的线段,统计竖线内每段线段的长度,累加即可。先把所有端点的横坐标放入数组X里, 然后对线段两两求交,把交点的横坐标也放入数组X里,然后排序去重。求出每条线段与竖线的交点的y坐标的最大值,因为竖线可能与多条线段相交,但只有最 上面的点是可见的。
Conclusion:判断直线相交改为判断直线不平行会简化很多,同时用到了离散化的思想
Code:

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-7;
typedef struct Point
{
    double x, y;
    Point(double x = 0.0, double y = 0.0): x(x), y(y) {}
} Vector;
struct Line
{
    Point a, b;
    Line() {}
    Line(Point a, Point b): a(a), b(b) {}
} L[5000];
Vector operator + (Vector A, Vector B)
{
    return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Vector A, Vector B)
{
    return Vector(A.x - B.x, A.y - B.y);
}
Vector operator * (Vector A, double t)
{
    return Vector(A.x * t, A.y * t);
}
Vector operator / (Vector A, double t)
{
    return Vector(A.x / t, A.y / t);
}
int dcmp(double x)
{
    if (fabs(x) < eps)
        return 0;
    return x < 0.0 ? -1 : 1;
}
double Dot(Vector A, Vector B)
{
    return A.x * B.x + A.y * B.y;
}
double Cross(Vector A, Vector B)
{
    return A.x * B.y - A.y * B.x;
}
bool segment(Point a1, Point a2, Point b1, Point b2)
{
    return dcmp(Cross(a1 - a2, b1 - b2)) != 0;
}
Point get(Point p, Vector v, Point q, Vector w)
{
    Vector u = p - q;
    double t = Cross(w, u) / Cross(v, w);
    return p + v * t;
}
bool onsegment(Point p, Point a1, Point a2)
{
    return dcmp(Cross(a1 - p, a2 - p)) == 0 && dcmp(Dot(a1 - p, a2 - p)) <= 0;
}
int dex = 0, fix = 0;
double H(double x)//求横坐标为x时,对应的线段上的y坐标的最大值
{
    double ans = 0.0;
    for (int i = 0; i < dex; i++)
    {
        if (segment(Point(x, -1000.0), Point(x, 1000.0), L[i].a, L[i].b))
        {
            Point tmp = get(Point(x, -1000.0), Vector(0.0, 2000), L[i].a, Vector(L[i].b - L[i].a));
            if (onsegment(tmp, L[i].a, L[i].b))
            {
                ans = max(ans, tmp.y);
            }
        }
    }
    return ans;
}
double X[500000];
int main()
{
    int n, cas = 1;
    while (scanf("%d", &n), n)
    {
        dex = fix = 0;
        for (int i = 0; i < n; i++)
        {
            double x, h, b;
            scanf("%lf%lf%lf", &x, &h, &b);
            L[dex++] = Line(Point(x - 0.5 * b, 0.0), Point(x, h));
            L[dex++] = Line(Point(x, h), Point(x + 0.5 * b, 0.0));
            X[fix++] = x - 0.5 * b;
            X[fix++] = x;
            X[fix++] = x + 0.5 * b;
        }
        for (int i = 0; i < dex; i++)//求山与山与上的交点的横坐标
        {
            for (int j = i + 1; j < dex; j++)
            {
                if (segment(L[i].a, L[i].b, L[j].a, L[j].b))//如果相交
                {
                    Point tmp = get(L[i].a, L[i].b - L[i].a, L[j].a, L[j].b - L[j].a);//求交点
                    if (onsegment(tmp, L[i].a, L[i].b))//交点在线段上
                    {
                        X[fix++] = tmp.x;
                    }
                }
            }
        }
        sort(X, X + fix);//排序
        fix = unique(X, X + fix) - X;//去重
        double preh = H(X[0]);
        double ans = 0.0;
        for (int i = 1; i < fix; i++)//求所有竖线区间内的线段和
        {
            double nowh = H(X[i]);
            double disx = fabs(X[i] - X[i - 1]);
            double disy = fabs(nowh - preh);
            if (dcmp(preh) || dcmp(nowh))//判断是不是同一个点
            {
                ans += sqrt(disx * disx + disy * disy);
            }
            preh = nowh;
        }
        printf("Case %d: %.0f\n\n", cas++, ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值