杭电oj1874畅通工程续:Dijkstra与Floyd最短路径解题全解析
如果你刚开始刷图论里的最短路径多半会在某个题单里看到这个题号杭电oj1874题名叫《畅通工程续》。这道题在ACM入门圈子里出场率极高很多人对Dijkstra的第一印象就是它给的。它不考复杂的建模不涉及负权边、负环、多源汇这些花活就是一个干干净净的单源最短路径问题输入输出也很规整可以说是一道少见的“什么都给你配好了”的入门题。我第一次做这道题的时候还没领悟到“重边”这个坑随手写了个邻接矩阵就直接提交结果WA到怀疑人生。后来翻了讨论区才明白题目里的“可能有多条道路”是认真的——两条城镇之间完全可以重复修路你得自己取最小值。这也是我把这道题记到现在的原因越简单的题越能让你把基础细节扎牢。这篇文章就围绕杭电oj1874展开把题意、算法选型、朴素Dijkstra、堆优化Dijkstra、Floyd三种写法全部串一遍最后再聊聊那些最常见的WA原因和调试习惯。不管你是刚接触最短路径的初学者还是想把这题当模板复习的选手这篇都能直接拿来用。1. 题目到底在考什么先把题意读懂1.1 题面翻译城镇、道路和距离题面背景很直白某个地方有N个城镇编号从0到N-1M条双向大道连接其中一些城镇每条大道有一个路程长度X。现在给你一个起点城镇S和一个终点城镇T要求输出从S到T的最短路径长度如果根本无法到达就输出-1。看起来非常简单但它把所有最短路径算法的核心要素都覆盖了带权图、无向边、单源点、单终点、可能有多个测试用例。没有负权边没有负环不需要判断路径是否存在环路问题也不需要输出具体路径方案只需要最短距离数值。这样纯净的条件正是“教科书级”最短路径题的标配。做法上也基本没有分歧Dijkstra是首选Floyd也能过数据范围不大时两者都能在1秒内跑完。这题难的不是算法本身而是你能不能把读入、初始化、边界判断这些容易被忽略的细节全部处理好。1.2 隐藏的输入输出细节读题容易忽略的点读题不仔细的话有三个点特别容易踩第一多组输入。题目说“输入包含多组测试数据”所以代码必须写成while (scanf(...) ! EOF)或者while (cin n m)只处理一组数据就退出程序的话本地样例能过一提交就是Output Limit Exceeded或者运行结果完全对不上。第二城镇编号从0开始。很多人习惯把数组下标从1开始用于是读入的城镇编号也下意识减一结果越界或者路径对不上。这题的城镇就是0到N-1直接当下标用就行。第三输出格式要求“每组输出占一行”S和T之间没有多余空格。看起来是废话但如果你在输出后面多打了一个空格遇到严格判题时也会WA。2. 选哪种最短路径算法Dijkstra还是Floyd2.1 三种可选思路对比针对这道题主流解法有三种朴素Dijkstra、堆优化Dijkstra、Floyd-Warshall。我先用一张表把它们的关键差异摆出来后面再逐个展开。算法时间复杂度代码量适用场景本题表现朴素DijkstraO(N^2)少稠密图、小数据范围稳过堆优化DijkstraO((NM)logN)中稀疏图、大数据范围稳过Floyd-WarshallO(N^3)极少多源最短路、小数据范围能过从图上来看N大概在200以内M也没有大到离谱所以理论上三种都能跑完。floyd在N200时的复杂度是800万次左右1秒内完全没压力朴素Dijkstra在N200时更是只算4万次级别可以说快得没有任何悬念。2.2 为什么说这是Dijkstra的“教科书题”Dijkstra算法的要求是图中不能有负权边因为它是基于“已确定最短距离的点不会再次被更新”这个贪心思想运行的。一旦出现负权边这条性质就被打破算法会给出错误结果。这道题的道路长度X显然是非负的天然满足Dijkstra的适用条件。同时题目是单源点求最短路径正好命中Dijkstra的典型场景。你用Floyd也能做但它会计算所有点对之间的最短距离等于多算了很多不必要的信息用Bellman-Ford也能做但每条边要反复松弛N-1轮效率上完全没必要。朴素Dijkstra在理解难度和代码量之间取得了最舒服的平衡所以绝大多数题解都会选它作为标准做法。3. 核心代码实现朴素Dijkstra邻接矩阵版3.1 初始化与数据读取邻接矩阵版的重点在于初始化要干净、重边要取最小。我们先看完整的可提交代码再按步骤拆开解释。#include cstdio #include cstring #include algorithm using namespace std; const int INF 0x3f3f3f3f; const int MAXN 205; int n, m; int g[MAXN][MAXN]; int dist[MAXN]; bool vis[MAXN]; void dijkstra(int s) { memset(dist, 0x3f, sizeof(dist)); memset(vis, 0, sizeof(vis)); dist[s] 0; for (int i 0; i n; i) { int u -1; int minv INF; for (int j 0; j n; j) { if (!vis[j] dist[j] minv) { minv dist[j]; u j; } } if (u -1) break; vis[u] true; for (int v 0; v n; v) { if (!vis[v] g[u][v] ! INF dist[u] g[u][v] dist[v]) { dist[v] dist[u] g[u][v]; } } } } int main() { while (scanf(%d%d, n, m) ! EOF) { memset(g, 0x3f, sizeof(g)); for (int i 0; i m; i) { int a, b, x; scanf(%d%d%d, a, b, x); g[a][b] min(g[a][b], x); g[b][a] min(g[b][a], x); } int s, t; scanf(%d%d, s, t); dijkstra(s); if (dist[t] INF) { printf(-1\n); } else { printf(%d\n, dist[t]); } } return 0; }3.2 主循环与松弛操作Dijkstra的主循环做的事情不多核心就两步找当前未访问点中距离最小的点u然后用u去松弛所有相邻点v。找最小点的过程就是代码里那个minv和u的循环。每次遍历所有点挑出未访问且dist[j]最小的那个。如果找不到未访问点u -1说明剩下的点都不可达直接跳出循环。松弛操作的内层循环里有一个很多人容易忽略的细节g[u][v] ! INF这个判断。虽然dist[u] g[u][v]在两个值都是INF时也不会超过int上限因为INF 0x3f3f3f3f两倍约为21.2亿仍在int范围内但加上这个判断可以让逻辑更清楚也避免无意义的加法。同时!vis[v]保证每个节点只会被当作终点更新一次这就是Dijkstra贪心性质的体现。3.3 为什么INF选0x3f3f3f3f很多初学的人纠结INF到底取多少。这道题里取0x3f3f3f3f是非常合适的理由有两点第一它大约等于10.6亿比任何可能的真实路径距离都大得多。即使N再大一点所有边的长度和也不会超过这个数量级。第二0x3f3f3f3f的每个字节都是0x3f所以可以直接用memset(g, 0x3f, sizeof(g))按字节填充。如果你用0x7fffffff作为INFmemset就只能填0x7f得到的其实是0x7f7f7f7f而不是0x7fffffff数值变了后续比较就全乱套。输出那里也要注意判断dist[t] INF用的是严格等于而不是大于等于某个阈值。因为只要起点和终点之间没有任何一条可达路径dist[t]就会一直保留初始化时的INF值不会在松弛过程中被改成其他“恰好很大”的数。这个逻辑不需要模糊处理等号判断完全准确。4. 进阶版本邻接表 堆优化Dijkstra4.1 什么时候需要堆优化朴素Dijkstra在N等于200时跑得飞快但如果哪天刷到类似的题N变成2000、20000朴素版O(N^2)就会开始吃力。这时候需要用优先队列堆来维护“当前距离最小的未访问点”把“每次找最小点”的O(N)降低到O(logN)。堆优化Dijkstra的整体复杂度是O((NM)logN)在稀疏图里优势特别明显。比如M只有2N级别的图朴素版要跑4亿次循环的地方堆优化版可能几百万次就搞定。4.2 优先队列的小技巧堆优化版的代码相比朴素版只是换了一个数据结构核心思想没变。这里给出一个针对本题数据的版本#include cstdio #include cstring #include vector #include queue using namespace std; const int INF 0x3f3f3f3f; const int MAXN 205; struct Edge { int to, w; }; vectorEdge adj[MAXN]; int dist[MAXN]; void dijkstra(int s) { memset(dist, 0x3f, sizeof(dist)); dist[s] 0; priority_queuepairint,int, vectorpairint,int, greaterpairint,int pq; pq.push({0, s}); while (!pq.empty()) { int d pq.top().first; int u pq.top().second; pq.pop(); if (d ! dist[u]) continue; for (const Edge e : adj[u]) { int v e.to; int w e.w; if (dist[u] w dist[v]) { dist[v] dist[u] w; pq.push({dist[v], v}); } } } } int main() { int n, m; while (scanf(%d%d, n, m) ! EOF) { for (int i 0; i n; i) adj[i].clear(); for (int i 0; i m; i) { int a, b, x; scanf(%d%d%d, a, b, x); adj[a].push_back({b, x}); adj[b].push_back({a, x}); } int s, t; scanf(%d%d, s, t); dijkstra(s); if (dist[t] INF) printf(-1\n); else printf(%d\n, dist[t]); } return 0; }priority_queue默认是大根堆存pair时会先比较first再比较second。用greater把它反转成小根堆后每次堆顶就是当前距离最小的点。pair的第一个元素存距离第二个元素存节点编号这个顺序不能反否则会先按节点编号排序完全失去堆优化的意义。if (d ! dist[u]) continue;这行是堆优化版的精髓专业说法叫“惰性删除”。因为同一个节点可能在更新过程中被多次压入堆中旧的记录已经失效但我们不需要手动删除堆里的元素只需要在弹出时检查当前距离和dist[u]是否一致不一致就跳过。这个技巧可以防止重复处理同一节点保证算法复杂度正确。5. 换条路走Floyd-Warshall也能做5.1 Floyd的等价写法如果你对Dijkstra还不太熟或者想顺便巩固一下多源最短路知识用Floyd也能过这道题。Floyd的思路极简枚举每个点k作为中转点看能不能通过k让i到j的距离变短。#include cstdio #include cstring #include algorithm using namespace std; const int INF 0x3f3f3f3f; const int MAXN 205; int g[MAXN][MAXN]; int main() { int n, m; while (scanf(%d%d, n, m) ! EOF) { memset(g, 0x3f, sizeof(g)); for (int i 0; i n; i) g[i][i] 0; for (int i 0; i m; i) { int a, b, x; scanf(%d%d%d, a, b, x); g[a][b] min(g[a][b], x); g[b][a] min(g[b][a], x); } for (int k 0; k n; k) { for (int i 0; i n; i) { for (int j 0; j n; j) { if (g[i][k] ! INF g[k][j] ! INF g[i][k] g[k][j] g[i][j]) { g[i][j] g[i][k] g[k][j]; } } } } int s, t; scanf(%d%d, s, t); if (g[s][t] INF) printf(-1\n); else printf(%d\n, g[s][t]); } return 0; }这里有一个必须先做的初始化g[i][i] 0。因为每个城镇到自己的距离当然是0矩阵默认全INF的话后面Floyd更新时会把自环距离算错。同时读入道路时依然要用min处理重边不能直接赋值。5.2 为什么Floyd在这种题里也够用Floyd的时间复杂度是O(N^3)听上去比Dijkstra的O(N^2)慢了一个量级但在本题数据范围内差距可以忽略。假设N最大是200三重循环最多执行800万次对现代CPU来说只要几毫秒到几十毫秒。而且Floyd有个隐藏的优势它计算的是所有点对之间的最短距离。如果题目后面再加上“多次询问任意两点之间的最短路径”Floyd只需要跑一次预处理每次查询直接查表O(1)返回反而比每次重新跑一遍Dijkstra更划算。所以在竞赛里看到“N小于300且询问次数特别多”的题第一反应不应该是Dijkstra而是Floyd。当然这道题只有一次询问用Floyd属于“杀鸡用牛刀”新手理解一下它是怎么实现“逐个中转点更新”的就够了真正比赛时还是优先Dijkstra更稳妥。6. 实战中的坑重边、自环、起终点相同6.1 重边的坑这是我这道题第一次WA的元凶。题目里并没有保证每条道路唯一两座城镇之间可能出现多条不同长度的路。如果你在读入时直接写g[a][b] x; g[b][a] x;后读入的路会覆盖先读入的路一旦先读入的是短路、后读入的是长路最短路径结果就直接偏大。正确写法是g[a][b] min(g[a][b], x);。这个细节在邻接矩阵版本里尤其关键因为矩阵每个位置只能存一个数。邻接表版本会好一些因为多条边都会保留Dijkstra在松弛时会分别比较不会算错但保留无用长边会浪费一点空间和时间。6.2 起终点相同的边界情况S等于T时结果应该是0。朴素Dijkstra和堆优化Dijkstra天然满足这一点因为dist[s] 0且不会通过任何边再更新0为负数。Floyd也一样因为初始化了g[i][i] 0。但如果你忘记初始化对角线或者某些模板里把dist初始化的循环写漏了起终点相同的测试数据就会翻车。很多题目数据里会刻意放这种边界case一旦你WA了却看不出原因可以先检查这类“想当然”的初始值问题。6.3 自环的影响自环就是某个城镇修了一条通往自己的道路。理论上这种路毫无意义因为它只会让路径变长。在读入时自环数据a b会写入g[a][a]如果刚好写入一个正数而后面没有用min初始化对角线g[a][a]就不再是0Dijkstra松弛时可能把dist[a]更新成一个错误值。解决办法有两个一是在读入前就把所有g[i][i]初始化为0二是在读入边时跳过a b的情况。个人建议两条都做反正不会影响正确性还能多一层防护。7. 常见问题与调试心得7.1 高频WA原因速查表把常见的失败原因整理成一张表提交前对着过一遍能省下很多翻讨论区的时间。问题现象可能原因修复方法样例过、提交WA重边未取min读入时用g[a][b] min(g[a][b], x)输出一个巨大数INF初始化错误使用0x3f3f3f3f并统一memset起终点相同但输出非0对角线未初始化为0初始化时让g[i][i] 0多组输入只跑了一组没用EOF循环改成while (scanf(...) ! EOF)数组越界城镇编号从0开始但数组开小MAXN设成205或更大Floyd结果偏大中转循环顺序写错k循环必须放在最外层7.2 个人调试小技巧刷题踩坑多了以后我养成了一个习惯遇到WA先从三个地方查——输入处理、INF常量、边界数据。每次顺序固定不随机翻代码效率反而更高。调试具体代码时不要只盯着最终输出可以临时打印dist数组看一下每个点到起点的距离值。比如这个题如果打印出来出现某个节点距离是0x3f3f3f3f说明它不在起点所在的连通块里如果出现某个不该是0的节点变成0多半是初始化写错了。用这种“过程性输出”定位比看最终结果瞎猜快得多。如果你有对拍条件还可以写一个暴力BFS或Floyd版本随机生成小规模数据对比两个程序的输出。这种做法在正式比赛里用不上但练习阶段对提升正确率帮助极大。8. 从这道题延伸出去最短路在真实场景还能做什么8.1 地图导航和交通规划中的最短路径学了最短路算法以后你会发现生活里到处都是它的影子。地图App从一个位置到另一个位置的路线规划本质上就是在一个巨大的带权图上跑最短路径边的权重可能是行驶时间、距离、拥堵程度等多种因素的综合值。生产环境里图的规模可能达到百万甚至千万级节点所以工程上会用到A*、双向Dijkstra、分层图等多种优化手段但核心思想依然是“每次确定一个临时最短距离最小的节点往外扩展”这套东西。物流配送的路径调度也是同样的思路。一个站点要给多个订单配送时除了距离还要考虑时间窗口、车辆载重等多种约束这时问题会变成带约束的车辆路径规划但底层仍然离不开单源最短路径的计算。8.2 后续可以继续刷的题与算法方向如果你把杭电oj1874彻底吃透了后面的学习路线可以这样安排先刷几道同类型的模板题把Dijkstra写成条件反射然后学Bellman-Ford了解负权边和负环的检测方式再学SPFA虽然它在竞赛圈口碑两极分化但理解一下队列优化的思想没有坏处。图论再深入的话还有最小生成树、拓扑排序、差分约束、网络流等方向每个方向都有对应的经典题。就我在实际做题过程中的体会来说最短路这类模板题没有太多花哨技巧真正的分水岭在于能不能把细节一次写对。重边取min、对角线初始化为0、多组数据的EOF循环、INF的选择——这四个点你做到肌肉记忆之后后面刷什么最短路径题都会顺手很多。最后再多说一句每次AC之后不要急着看下一题把自己WA的原因写下来比多刷十道题更值。

相关新闻

Go结构体对象创建全解析:从字面量到工厂函数与链式配置

Go结构体对象创建全解析:从字面量到工厂函数与链式配置

结构体这玩意儿,在 Go 里几乎无处不在。你写业务代码,定义个 User、Order、Config,用的是它;你读标准库源码,net/http 里的 Request、Response,还是它。可以说,把结构体用明白了,Go …

2026/10/12 9:16:53 阅读更多 →
Apriori算法实战:从购物篮分析到商业规则挖掘

Apriori算法实战:从购物篮分析到商业规则挖掘

1. 为什么超市收银台旁总摆着口香糖和巧克力?——Apriori算法的真实世界切口你有没有注意过,每次在超市结账时,收银台旁边永远少不了口香糖、薄荷糖、巧克力棒,甚至小包装的坚果?这不是偶然,也不是收银员的…

2026/10/12 9:16:53 阅读更多 →
Promise.withResolvers 生产实战:从根源彻底消除异步状态机中的外部闭包胶水代码

Promise.withResolvers 生产实战:从根源彻底消除异步状态机中的外部闭包胶水代码

在现代前端工程中,只要涉及到异步状态机、自定义并发队列、WebSocket 双向 RPC 封装,或者跨 Web Worker 的请求响应匹配,我们总会写出这样一段极度别扭的“闭包脱壳”代码: // 曾经几乎每个大厂库里都有的丑陋胶水代码 let resolv…

2026/10/12 9:16:53 阅读更多 →

最新新闻

基于模型压缩的识别算法Python源码:蒸馏与剪枝实战

基于模型压缩的识别算法Python源码:蒸馏与剪枝实战

简介:这份资源是面向高校毕业设计与深度学习入门者的模型压缩识别算法Python源码包,聚焦知识蒸馏与剪枝两类主流压缩技术,帮助读者在有限算力下完成识别模型的轻量化改造与对比实验。包内共185个文件,以79个py源码为核心&#xff…

2026/10/12 10:02:52 阅读更多 →
小波神经网络在光伏辐照预测中的工程落地实践

小波神经网络在光伏辐照预测中的工程落地实践

简介:本资源是一篇发表于《可再生能源》期刊的学术论文,面向新能源、电力系统、人工智能交叉领域的研究者与工程技术人员,聚焦太阳能发电中关键的超短期辐照强度预测难题。论文提出一种融合小波分析与神经网络优势的WNN模型,针对春…

2026/10/12 10:02:52 阅读更多 →
数学证明的事件化记录:构建可追溯、可验证的AI推理过程

数学证明的事件化记录:构建可追溯、可验证的AI推理过程

1. 这不是“AI写证明”,而是让数学推理过程真正可追溯、可验证、可复盘的底层重构“事件模型下AI数学证明的事件化记录与透明性架构”——这个标题里没有一个字是虚的,它直指当前AI数学推理系统最痛的软肋:我们能看到结论,却看不见…

2026/10/12 10:02:52 阅读更多 →
轻型AI中台:解决中小企业重复录入与对账困难的工程化方案

轻型AI中台:解决中小企业重复录入与对账困难的工程化方案

1. 项目概述:为什么一个“轻型AI中台”能真正解决财务与运营一线的痛感“部署轻型AI中台,消除重复录入、消减对账困难”——这句话不是PPT里的口号,而是我去年在三家中小制造企业、两家连锁零售服务商现场蹲点三个月后,亲手推出来…

2026/10/12 10:02:52 阅读更多 →
Hive外部表映射HBase已有数据实战指南

Hive外部表映射HBase已有数据实战指南

1. 项目概述:为什么非得用 Hive 外部表去“看” HBase 已有数据? 在某次数据平台升级中,我接手了一个典型的老系统迁移任务:上游业务系统多年积累的用户行为日志,早已稳定写入 HBase 集群,表名 user_acti…

2026/10/12 10:02:52 阅读更多 →
three.js编辑器开发实战:从渲染循环到撤销重做的完整指南

three.js编辑器开发实战:从渲染循环到撤销重做的完整指南

1. 为什么我不建议你从零手写渲染循环很多人第一次接触 three.js 编辑器开发,脑子里蹦出来的第一个念头是"我要自己写一套渲染管线"。这个想法本身没错,但如果你真的从requestAnimationFrame开始一行行搭,大概率会在第三天卡在&quo…

2026/10/12 10:01:51 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →