整个图每条街道都是双车道,相当于一对正反有向边,天然存在欧拉回路,只计算所有道路长度之和乘以2除以铲雪速度即可。
OJ: roj
题目 ID: 1374
难度:普及-
标签:欧拉回路图论贪心
创建: 2026-10-08 16:03
更新: 2026-10-08 16:03
形式化题目
给定一个无向图,代表城市的街道。图中给出了铲雪车的起点坐标 小时:分钟,其中分钟要四舍五入到整数且不足两位时补齐两位。
正解
思路
这道题表面上看似一道复杂的“中国邮递员问题(Chinese Postman Problem)”,实则是一个被“双向单车道”和“空载速度 50 km/h”这两个条件包装过的欧拉回路模型题。
题目中的关键句是:“所有街道都是双向一个车道”。这意味着如果我们把图看作一个有向图,那么每一条给出的街道,实际上就是两条连接两点但方向相反的有向边。
- 在这样的有向图中,无论原来的无向图有多少个度数为奇数的顶点,在转成有向图后,每一个顶点的“入度”和“出度”一定是相等的(因为每连出一条有向边,必定有一条反向的入边)。
- 题目又保证了“铲雪车从起点一定可以到达任何街道”,说明该图是强连通的。
这两个条件(入度等于出度,且强连通)恰好是有向图存在欧拉回路的充要条件。
存在欧拉回路,意味着铲雪车完全可以一笔画走遍所有的有向边(即每一条车道),不重复且不遗漏地完成铲雪任务,并最终回到起点。
因此,根本不需要像求解一般的中国邮递员问题那样去找奇度点并进行“奇度点配对”(因为所有的边都要走正反各一次),也根本不会发生以
解题步骤大大简化:
- 计算所有给定街道的单向直线距离(欧几里得距离)之和
total_distance(单位为米)。 - 因为是双向单车道,铲雪车走过的总路程就是所有街道单向距离之和的
倍,即 2 * total_distance米。 - 铲雪速度为
,即 。 - 总耗时(小时)为
;换算成分钟则是 。 - 将分钟数四舍五入到最近的整数,再换算成
小时:分钟格式输出。 注:输入的起点坐标对最短时间完全没有影响,仅读取消费掉即可。
代码
python
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-10-08 15:03
# update_at: 2026-10-08 15:03
import sys
from math import floor, hypot
SPEED = 20000 # 铲雪时速度 20 km/h,换算成米/小时
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
start_x, start_y = next(data), next(data) # 停车点坐标,对最短时间没有影响
# 每条街道给出 4 个整数 x1 y1 x2 y2;同一个迭代器连取四次就按街道分好组。
# 街道是双向单车道,拆成一正一反两条有向边后各点入度=出度,图有欧拉回路,
# 铲雪车能一路铲雪走遍全部车道再回起点,所以总路程 = 单向长度之和 × 2。
quads = zip(data, data, data, data) # 迭代器连取四次 => 每项是一条街道的 (x1,y1,x2,y2)
one_way = sum(hypot(x2 - x1, y2 - y1) for x1, y1, x2, y2 in quads)
minutes = floor(one_way * 2 / SPEED * 60 + 0.5) # 总用时换算成分钟,四舍五入
print(f"{minutes // 60}:{minutes % 60:02d}")
if __name__ == "__main__":
solve()cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-08 15:03
* update_at: 2026-10-08 15:03
*/
// 一本通 1374《铲雪车(snow)》
// 每条街道都是双向单车道,拆成一正一反两条有向边后每个顶点入度=出度,
// 又保证"从起点可达任何街道"(连通),所以图存在欧拉回路:
// 铲雪车能一路铲着雪走遍所有车道再回到起点,全程 20 km/h,不会有 50 km/h 的空驶。
// 答案 = 所有街道长度之和 × 2 ÷ 20000 小时(街道按双车道各走一次)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
double total_distance; // 所有街道的单向长度之和,单位米
// 读入停车点坐标(题意用不到,只按格式消费)与全部街道端点,累加街道长度
void read_input() {
ll start_x, start_y;
if (!(cin >> start_x >> start_y)) return;
ll x1, y1, x2, y2;
while (cin >> x1 >> y1 >> x2 >> y2) {
double dx = x1 - x2;
double dy = y1 - y2;
total_distance += sqrt(dx * dx + dy * dy);
}
}
// 时间(分钟) = 总路程(米) × 2 ÷ 20000(米/小时) × 60 = total_distance × 0.006,
// 四舍五入到整分钟,再按 小时:分钟 输出(分钟补两位零)
void solve() {
ll total_minutes = floor(total_distance * 0.006 + 0.5);
ll hour = total_minutes / 60;
ll minute = total_minutes % 60;
cout << hour << ":" << setfill('0') << setw(2) << minute << "\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
read_input();
solve();
return 0;
}复杂度
- 时间复杂度:
,其中 是给出的街道数量。只需要一次遍历读取输入数据并累加距离计算。 - 空间复杂度:
,只需保存累计长度和当前读取的坐标点即可。
总结
- 为什么不需要奇度点配对:这是本题最容易踩的坑。如果是“单车道”街道网络,且要求走遍所有边并回到起点,那么图的奇度点就不为
,这就变成了经典的“中国邮递员问题”,需要求最短路来做奇度点匹配,走回头路时便会用到 的空驶速度。但本题题面强调了“所有街道都是双车道”,这就相当于每条边必定走两次(一正一反),把奇度点全都自然地变成了出入度相等的点。因此,“不需要任何配对空跑,总路程就是所有边长的两倍”才是正解。 - 数据量级与变量类型:当给出的街道数目多、坐标范围大时(如大数据点),计算出的距离和累计的时间(尤其是分钟总数)可能会很大。比如测试点中有的总耗时达到上万小时。因此,在存储坐标和分钟总数时,应使用
位整数(如 C++ 中的 long long)来避免溢出。 - 四舍五入:最后将浮点型分钟数转为整数时,需要正确使用四舍五入。C++ 中可以用
floor(minutes + 0.5)或者是round(minutes),特别要注意部分小数可能处于四舍五入的边界附近。 - 输出格式控制:最终输出结果必须以
时:分的格式,当分钟数小于时必须用前导零补齐两位,即 %02d格式。