题目描述
一只囊地鼠需要从起点 (xs,ys)(x_s, y_s)(xs,ys) 移动到终点 (xt,yt)(x_t, y_t)(xt,yt),途中会遇到多个被水淹没的沟渠。坐标系中,xxx 轴从西向东,yyy 轴从南向北。
干燥陆地的分布有特殊的规律:对于任意整数 nnn,当 n×D+L≤y≤(n+1)×D−Ln \times D + L \leq y \leq (n+1) \times D - Ln×D+L≤y≤(n+1)×D−L 时,点 (x,y)(x, y)(x,y) 位于干燥陆地上。沟渠位于这些干燥带之间,每条沟渠的宽度为 2L2L2L。
囊地鼠的目标是:
- 最小化游泳距离(穿过沟渠的总宽度)
- 在满足条件 111 的前提下,最小化在干燥陆地上的行走距离
给定 DDD, LLL, xsx_sxs, ysy_sys, xtx_txt, yty_tyt,求囊地鼠的最优路线。
输入格式
每行包含 666 个实数:DDD, LLL, xsx_sxs, ysy_sys, xtx_txt, yty_tyt。
输出格式
对于每组输入,输出一行,格式为:
The gopher has to swim {swim_dist} meters and walk {walk_dist} meters.
题目分析
关键观察
-
游泳距离的确定性:
- 游泳距离只取决于必须穿过的沟渠数量
- 从起点所在的带到终点所在的带,必须穿过 k=∣ns−nt∣k = |n_s - n_t|k=∣ns−nt∣ 条沟渠
- 每条沟渠宽度为 2L2L2L,因此最小游泳距离为 k×2Lk \times 2Lk×2L
-
几何本质:
- 为了保持最小游泳距离,必须在每个沟渠处垂直游泳
- 这相当于将每个游泳段"压缩"掉,将终点向起点方向移动总游泳距离
- 最优行走路径就是连接起点和虚拟终点的直线
数学模型
设起点所在的带编号为 ns=⌊ys/D⌋n_s = \lfloor y_s / D \rfloorns=⌊ys/D⌋,终点所在的带编号为 nt=⌊yt/D⌋n_t = \lfloor y_t / D \rfloornt=⌊yt/D⌋,则:
- 必须穿过的沟渠数量:k=∣ns−nt∣k = |n_s - n_t|k=∣ns−nt∣
- 游泳距离:swim=k×2Lswim = k \times 2Lswim=k×2L
- 虚拟终点的 yyy 坐标:
- 如果 ns<ntn_s < n_tns<nt(向上走):yv=yt−k×2Ly_v = y_t - k \times 2Lyv=yt−k×2L
- 如果 ns>ntn_s > n_tns>nt(向下走):yv=yt+k×2Ly_v = y_t + k \times 2Lyv=yt+k×2L
- 如果 ns=ntn_s = n_tns=nt:yv=yty_v = y_tyv=yt
- 行走距离:walk=(xt−xs)2+(yv−ys)2walk = \sqrt{(x_t - x_s)^2 + (y_v - y_s)^2}walk=(xt−xs)2+(yv−ys)2
算法思路
- 计算起点和终点所在的带编号
- 计算必须穿过的沟渠数量 kkk
- 计算游泳距离 k×2Lk \times 2Lk×2L
- 根据行进方向计算虚拟终点的 yyy 坐标
- 计算起点到虚拟终点的欧几里得距离作为行走距离
复杂度分析
- 时间复杂度:O(1)O(1)O(1),只有常数次计算
- 空间复杂度:O(1)O(1)O(1),只使用常数个变量
代码实现
// Swimming Gopher
// UVa ID: 10488
// Verdict: Accepted
// Submission Date: 2025-11-16
// UVa Run Time: 0.000s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net
#include <bits/stdc++.h>
using namespace std;
int main() {
double D, L, xs, ys, xt, yt;
while (cin >> D >> L >> xs >> ys >> xt >> yt) {
int k = abs((int)floor(ys / D) - (int)floor(yt / D));
double swim = k * 2 * L;
double yv = yt - (yt > ys ? k * 2 * L : -k * 2 * L);
double walk = hypot(xt - xs, yv - ys);
printf("The gopher has to swim %.2lf meters and walk %.2lf meters.\n", swim, walk);
}
return 0;
}
样例验证
样例1
输入:100.0 10.0 0.0 50.0 100.0 50.0
输出:The gopher has to swim 0.00 meters and walk 100.00 meters.
- 起点和终点在同一带,无需游泳
- 行走距离为直线距离 100.00100.00100.00
样例2
输入:100.0 10.0 0.0 50.0 100.0 150.0
输出:The gopher has to swim 20.00 meters and walk 128.06 meters.
- k=1k = 1k=1,游泳距离 20.0020.0020.00
- 虚拟终点 yyy 坐标:150−20=130150 - 20 = 130150−20=130
- 行走距离:1002+(130−50)2=10000+6400≈128.06\sqrt{100^2 + (130-50)^2} = \sqrt{10000 + 6400} \approx 128.061002+(130−50)2=10000+6400≈128.06
样例3
输入:100.0 10.0 0.0 -50.0 100.0 50.0
输出:The gopher has to swim 20.00 meters and walk 128.06 meters.
- k=1k = 1k=1,游泳距离 20.0020.0020.00
- 虚拟终点 yyy 坐标:50−20=3050 - 20 = 3050−20=30
- 行走距离:1002+(30−(−50))2=10000+6400≈128.06\sqrt{100^2 + (30-(-50))^2} = \sqrt{10000 + 6400} \approx 128.061002+(30−(−50))2=10000+6400≈128.06
样例4
输入:100.0 10.0 0.0 -50.0 100.0 150.0
输出:The gopher has to swim 40.00 meters and walk 188.68 meters.
- k=2k = 2k=2,游泳距离 40.0040.0040.00
- 虚拟终点 yyy 坐标:150−40=110150 - 40 = 110150−40=110
- 行走距离:1002+(110−(−50))2=10000+25600≈188.68\sqrt{100^2 + (110-(-50))^2} = \sqrt{10000 + 25600} \approx 188.681002+(110−(−50))2=10000+25600≈188.68
总结
本题的关键在于理解几何变换的本质:将固定的游泳距离"压缩"后,问题转化为在干燥陆地上寻找起点到虚拟终点的最短直线路径。这种思路不仅简化了计算,还体现了优化问题中常见的"问题转化"思想。
9602

被折叠的 条评论
为什么被折叠?



