UVa1409/LA4019 Go to Class

UVa1409/LA4019 Go to Class

题目链接

本题是2007年icpc亚洲区域赛成都赛区G

题意

给出一个校园,校园中有很多纵横道路,横的平行于 x 轴,竖的平行于 y 轴。横向道路有 n 条,纵向道路有 m 条(1≤n,m≤25)。道路是有宽度的,并且大小不一。道路之间是草坪。现给出所有道路以及宿舍和教室的坐标,求宿舍到教室且不经过草坪的最短距离。

分析

需要用到一个结论:起点和终点需要走折线到达时,最优路线的那些转折点一定是纵横道路的交点。

先证明只有一条纵向道路和一条横向道路的简单情况,不妨设起点A在横向道路的北方,终点B在纵向道路的西方,见下图所示:

转折点只能在AP、BP的延长线和道路边界围起来的区域内选取,延长AP交BC于Q,则 { A C + C Q > A Q B Q + P Q > B P A Q = A P + P Q B C = B Q + C Q ⇒ A C + C Q + B Q + P Q > A P + P Q + B P ⇒ A C + B C + P Q > A P + P Q + B P ⇒ A C + B C > A P + B P \begin{align*}\\& \left\{\begin{matrix}AC+CQ>AQ\\BQ+PQ>BP\\AQ=AP+PQ\\BC=BQ+CQ\end{matrix}\right.\\& \Rightarrow AC+CQ+BQ+PQ>AP+PQ+BP\\& \Rightarrow AC+BC+PQ>AP+PQ+BP\\& \Rightarrow AC+BC>AP+BP \end{align*} ⎩ ⎨ ⎧AC+CQ>AQBQ+PQ>BPAQ=AP+PQBC=BQ+CQ⇒AC+CQ+BQ+PQ>AP+PQ+BP⇒AC+BC+PQ>AP+PQ+BP⇒AC+BC>AP+BP

可以将这个结论推广到有多条纵横道路的情况(用反证法),假设最优路线存在一个不是纵横道路交点的转折点C,如下图所示:

总是可以用纵横道路的交点P替换C从而更优。

至此,本题可以离散化处理后建图,用 dijkstra 求最短路即可。顶点集是起点、终点、各个纵横道路的交点,对直接可达的两点连边时有一个小难点:如何判断两个点直接可达?判断点 ( x 1 , y 1 ) 、 ( x 2 , y 2 ) (x_1,y_1)、(x_2,y_2) (x1,y1)、(x2,y2) 是否直接可达可以按如下思路:

  • 如果两点处在同一条横向道路(或者纵向道路)内,则直接可达;
  • 否则
    • x1==x2 || y1==y2,则不是直接可达;
    • 否则,将两点所在线段按照纵向道路的 x 坐标离散化切割成若干小段
      • 不在纵向道路内的每一小段必须全部包含在某一条横向道路内(不能有露出的部分),则直接可达;
      • 否则不是直接可达;

判断是否直接可达的逻辑实现要注意各种细节,特别是数组越界检查。贴一份生成测试数据的 python 脚本:

python 复制代码
# coding=utf-8

from random import randint

# X, N = 100000, 25
X, N = 12, 4

def out(p, x, y):
    for i in range(0, len(x), 2):
        if p[0] >= x[i] and p[0] <= x[i+1]:
            return False
    for i in range(0, len(y), 2):
        if p[1] >= y[i] and p[1] <= y[i+1]:
            return False
    return True

def gen():
    n, m, y, x = randint(1, N), randint(1, N), [], []
    for i in range(2*n):
        f = True
        while f:
            f = False
            v = randint(-X, X)
            for j in range(i):
                if y[j] == v:
                    f = True
                    break
            if not f:
                y.append(v)
    for i in range(2*m):
        f = True
        while f:
            f = False
            v = randint(-X, X)
            for j in range(i):
                if x[j] == v:
                    f = True
                    break
            if not f:
                x.append(v)
    y.sort()
    x.sort()
    s, t = (randint(-X, X), randint(-X, X)), (randint(-X, X), randint(-X, X))
    while out(s, x, y):
        s = (randint(-X, X), randint(-X, X))
    while out(t, x, y):
        t = (randint(-X, X), randint(-X, X))
    return n, m, s, t, y, x

if __name__ == '__main__':
    with open('in.txt', 'w') as f:
        for _ in range(10):
            n, m, s, t, y, x = gen()
            f.write(f'{n} {m}\n{s[0]} {s[1]} {t[0]} {t[1]}\n{y[0]}')
            for i in range(1, 2*n):
                f.write(f' {y[i]}')
            f.write(f'\n{x[0]}')
            for i in range(1, 2*m):
                f.write(f' {x[i]}')
            f.write(f'\n')
        f.write('0 0\n')

AC 代码

cpp 复制代码
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <queue>
#include <cmath>
using namespace std;

#define M 2510
#define N 55
int x[N], y[N], xs, ys, xt, yt, n, m, t, kase = 0; double d[M]; bool f[M];
struct node {
    double d; int u;
    bool operator< (const node& rhs) const {
        return d > rhs.d;
    }
};

bool check(int x1, int y1, int x2, int y2) {
    int k = lower_bound(x, x+m, max(x1, x2)) - x;
    if (k < m && ((x1==x2 && x[k]==x1) || (k&1 && k == upper_bound(x, x+m, min(x1, x2)) - x))) return true;
    k = lower_bound(y, y+n, max(y1, y2)) - y;
    if (k < n && ((y1==y2 && y[k]==y1) || (k&1 && k == upper_bound(y, y+n, min(y1, y2)) - y))) return true;
    if (x1 == x2 || y1 == y2) return false;
    if (x1 > x2) swap(x1, x2), swap(y1, y2);
    long long d = x2 - x1;
    k = upper_bound(x, x+m, x1) - x;
    int a = k==0 ? x1 : max(x[k-1],x1), b = k==m ? x2 : x[k];
    if (y1 < y2) {
        for (int i=0; a<x2; a=b, b = ++k>=m ? x2 : min(x[k],x2)) if (k == m || ~k&1) {
            while (d*y[i] <= (a - x1)*(long long)y2 + (x2 - a)*(long long)y1) ++i;
            if (~i&1 || d*y[i] < (b - x1)*(long long)y2 + (x2 - b)*(long long)y1) return false;
        }
    } else for (int i=n-1; a<x2; a=b, b = ++k>=m ? x2 : min(x[k],x2)) if (k == m || ~k&1) {
        while (d*y[i] >= (a - x1)*(long long)y2 + (x2 - a)*(long long)y1) --i;
        if (i&1 || d*y[i] > (b - x1)*(long long)y2 + (x2 - b)*(long long)y1) return false;
    }
    return true;
}

double dis(int x1, int y1, int x2, int y2) {
    double dx = x1-x2, dy = y1-y2;
    return sqrt(dx*dx+dy*dy);
}

double solve() {
    cin >> xs >> ys >> xt >> yt; n <<= 1; m <<= 1; t = m*n;
    for (int i=0; i<n; ++i) cin >> y[i];
    for (int i=0; i<m; ++i) cin >> x[i];
    sort(x, x+m); sort(y, y+n);
    if (check(xs, ys, xt, yt)) return dis(xs, ys, xt, yt);
    priority_queue<node> q; d[t] = 1e30; f[t] = false;
    for (int i=0, k=0; i<m; ++i) for (int j=0; j<n; ++j, ++k) {
        if (check(xs, ys, x[i], y[j])) q.push({d[k] = dis(xs, ys, x[i], y[j]), k});
        else d[k] = 1e30;
        f[k] = false;
    }
    while (!q.empty()) {
        int u = q.top().u; q.pop();
        if (u == t) break;
        if (f[u]) continue;
        f[u] = 1;
        int x1 = x[u/n], y1 = y[u%n];
        if (check(x1, y1, xt, yt)) {
            double d1 = d[u] + dis(x1, y1, xt, yt);
            if (d[t] > d1) q.push({d[t] = d1, t});
        }
        for (int i=0, k=0; i<m; ++i) for (int j=0; j<n; ++j, ++k) if (!f[k] && check(x1, y1, x[i], y[j])) {
            double d1 = d[u] + dis(x1, y1, x[i], y[j]);
            if (d[k] > d1) q.push({d[k] = d1, k});
        }
    }
    return d[t];
}

int main() {
    cout << fixed << setprecision(4);
    while (cin >> n >> m && n) cout << "Case " << ++kase << ": " << solve() << endl;
    return 0;
}
相关推荐
罗湖老棍子16 小时前
【例4-6】香甜的黄油(信息学奥赛一本通- P1345)
算法·图论·dijkstra·floyd·最短路算法·bellman ford
惆怅客1234 天前
icpc中求解各种搜索问题的算法
迭代加深搜索·搜索·icpc·启发式搜索·uva·对抗搜索·dlx
闻缺陷则喜何志丹6 天前
【三维建模】三维建模基础一
c#·计算几何·cad·三维建模·布尔运算·切点
闻缺陷则喜何志丹8 天前
【计算几何 最短路 动态规划】P1354 房间最短路问题
数学·算法·动态规划·最短路·计算几何·洛谷
闻缺陷则喜何志丹8 天前
计算几何汇总
c++·数学·计算几何·凸多边形·简单多边形
闻缺陷则喜何志丹9 天前
【2025博客之星】求职总结
线性代数·数学·计算几何·objectarx·cad·高度数学
闻缺陷则喜何志丹10 天前
【计算几何 化环为链】P14165 [ICPC 2022 Nanjing R] 清空水箱|普及+
c++·数学·算法·计算几何·洛谷·化环为链
llz_11215 天前
图(邻接表)-(DFS/BFS)-Dijkstra
算法·深度优先·dijkstra·宽度优先
闻缺陷则喜何志丹16 天前
【计算几何 二分查找】P5485 [JLOI2010] 铁人双项比赛|普及+
c++·数学·二分查找·计算几何·洛谷