2014百度之星复赛解题报告复赛:Race

Race
时间限制:10s  内存限制:64MB

问题描述
度度熊最近参加了一场劲跑比赛,但是这个劲跑比赛的规则比较特殊。比赛方预先在地上画了一些横线和竖线(可以认为这些线为无限长的直线),要求选手从指定的位置出发,在最短时间内按照规定的顺序经过所有的直线(只要到达直线上的任意一点即为经过)。为了帮助度度熊获胜,你能为他规划一条最短的线路吗?

输入
第一行为T,表示输入数据组数。
下面T组数据。每组数据中:
第一行为一个整数n,表示有n条直线。接下来n行,每行包含两个整数,表示一条直线。如果直线为竖线,则第一个整数为0,第二个整数为直线的横坐标x。如果直线为横线,则第一个整数为1,第二个整数为直线的纵坐标y。度度熊将从平面的原点出发。

输出
对第i组数据,输出
Case #i:
然后输出一个实数,表示从原点出发按输入顺序经过所有直线的最短路径的长度。保留到小数点后3位。

限制条件
1<=T<=10
1<=n<=10^6
-500<=x,y<=500

样例输入
1
3
0 1
1 1
0 2

样例输出
Case #1:
2.236

提示
度度熊需要按照规定的顺序经过所有的直线。如果度度熊不按照顺序经过直线,那么提前经过的直线是不算数的。例如,假设度度熊需要按顺序经过直线1和直线2,那么如果度度熊在经过直线2之前经过了直线1,这是不算数的,度度熊仍然需要在经过直线2后再次经过直线1。但是度度熊可以经过直线1和直线2的交叉点而同时都算数。

解题报告:
首先对横线和竖线的坐标分别做镜像翻装,使它们的坐标分别都是单调(非严格)递增的,并且相邻的横线和相邻的竖线的距离跟原始坐标的距离保持一致。这可以在线性时间内完成,并且不影响答案。然后按照顺序扫描所有直线,维护从原点出发的两条移动路径。一条移动路径尽可能往左走,一条移动路径尽可能往右走,但都保持最短路并且满足规定的直线到达顺序。这两条移动路径分别是上凸曲线和下凸曲线,每扫描到方向不同的两条直线时就根据交叉点更新其中一条移动路径。扫描到最后一条直线时就可以算出最短的移动路径长度了。


解题代码:
#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

const int MAX_N = 1000000;

struct Point {
    int x, y;
    Point() {
        x = 0;
        y = 0;
    }
    Point(int x_, int y_) {
        x = x_;
        y = y_;
    }
};

int n;
int dirs[MAX_N];
int lines[MAX_N];

inline long long cross_product(Point a, Point b) {
    return (long long)a.x * b.y - (long long)b.x * a.y;
}

inline long long cross_product(Point a, Point b, Point c) {
    return cross_product(Point(b.x - a.x, b.y - a.y), Point(c.x - a.x, c.y - a.y));
}

inline double distance(Point a, Point b) {
    return sqrt(double(a.x - b.x) * (a.x - b.x) + double(a.y - b.y) * (a.y - b.y));
}

void preprocess_lines() {
    int lastlines[2] = {0, 0};
    int lastrawlines[2] = {0, 0};
    for (int i = 0; i < n; i++) {
        int dir = dirs[i];
        int rawline = lines[i];
        lines[i] = lastlines[dir] + abs(rawline - lastrawlines[dir]);
        lastrawlines[dir] = rawline;
        lastlines[dir] = lines[i];
    }
}

double solve() {
    static Point lstack[MAX_N];
    static Point rstack[MAX_N];
    int lhead, ltail, rhead, rtail;
    lhead = ltail = rhead = rtail = 0;
    lstack[ltail++] = Point(0, 0);
    rstack[rtail++] = Point(0, 0);
    double result = 0;

    for (int i = 1; i < n; i++) {
        if (dirs[i] == 1 && dirs[i - 1] == 0) {
            Point pos(lines[i - 1], lines[i]);
            while (lhead + 2 <= ltail && cross_product(lstack[ltail - 2], lstack[ltail - 1], pos) <= 0) {
                ltail--;
            }
            lstack[ltail++] = pos;
            while (lhead + 2 == ltail && rhead + 2 <= rtail && cross_product(lstack[lhead], pos, rstack[rhead + 1]) >= 0) {
                result += ::distance(lstack[lhead], rstack[++rhead]);
                lstack[lhead] = rstack[rhead];
            }
        }
        if (dirs[i] == 0 && dirs[i - 1] == 1) {
            Point pos(lines[i], lines[i - 1]);
            while (rhead + 2 <= rtail && cross_product(rstack[rtail - 2], rstack[rtail - 1], pos) >= 0) {
                rtail--;
            }
            rstack[rtail++] = pos;
            while (rhead + 2 == rtail && lhead + 2 <= ltail && cross_product(rstack[rhead], pos, lstack[lhead + 1]) <= 0) {
                result += ::distance(rstack[rhead], lstack[++lhead]);
                rstack[rhead] = lstack[lhead];
            }
        }
    }

    if (dirs[n - 1] == 1) {
        for (int i = lhead; i + 1 < ltail; i++) {
            result += ::distance(lstack[i], lstack[i + 1]);
        }
        result += lines[n - 1] - lstack[ltail - 1].y;
    }
    if (dirs[n - 1] == 0) {
        for (int i = rhead; i + 1 < rtail; i++) {
            result += ::distance(rstack[i], rstack[i + 1]);
        }
        result += lines[n - 1] - rstack[rtail - 1].x;
    }

    return result;
}

int main() {
    int TT, NN;
    scanf("%d", &NN);
    for (TT = 1; TT <= NN; TT++) {
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d%d", &dirs[i], &lines[i]);
        }
        preprocess_lines();
        double ans = solve();
        printf("Case #%d:
", TT);
        printf("%.3f
", ans);
    }

    return 0;
}

原文地址:https://www.cnblogs.com/hosealeo/p/4190490.html