当前位置: 首页 > news >正文

ZR 25 noip D1T2 题解 | 最短路

传送门

题意

给出一个 \(n\) 个点 \(m\) 条边的无向图,边有边权,经过一条边 \((u,v,w)\) 的代价是 \(\min(走到u的路径上的最小边权,w)\)。(即前缀最小)
问从 \(1\) 走到 \(n\) 的最短路。

思路

肯定是要模仿 dijikstra 的过程来做。我们 dijikstra 时,是拿一条边 \((u,v,w)\) 松弛,来更新 \(dis_v\),是考虑边的贡献。

显然,最后走出来路径的边权一定是若干连续段。
一个连续段的贡献取决于:

  • 开头的边权
  • 连续段长度

用到连续段长度,记 \(len_{u,v}\) 表示 \(u\)\(v\) 的经过边个数最小,可以 floyd \(O(n ^ 3)\) 来计算。

我们在 dijikstra 时,对每条边 \((u,v,w)\) 去考虑它作为一个连续段的开头时的贡献。
要枚举连续段的结尾那个点 \(t\),这样,整个连续段的权值就是 \((len_{v,t}+1) \times w\)
接着,就可以用 \(dis_u + (len_{v, t} + 1) \times w\) 来更新 \(dis_t\)

另一种思考方式(by DE_aemmprty)

不从边入手,从贡献序列入手

观察贡献序列(路径上边权序列),发现是连续段,一个从 \(u\)\(v\) 的连续段要求路径上所有的边权 \(>= w_0\),其中 \(w_0\) 是开头的边权。

发现如果一个从 \(u\)\(v\) 的路径不满足这样的约束,即 \(\exists w_1 \in E_{u \rightarrow v} < w_0\),那么从 \(w_1\) 处断开形成新的连续段一定优于这种方案。
也就是说,这种方案就算考虑了也不会有影响,而想排除掉很难,于是可以不管,然后执行前文的算法流程即可。

这也是一个 trick:最优性问题中,当方案有不好处理的限制条件时,若不考虑该限制不会让方案变优,则可以忽视。

分析复杂度,插入到优先队列里的插入量是 \(O(nm)\) 的(因为每条边都会枚举 \(t\),分别是 \(O(m)\)\(O(n)\)),总复杂度就是 \(O(n^3+nm \log (nm))\)。显然起飞。

考虑优化,上述过程实现时,是每次从优先队列中取出 \(u\),枚举 \(u\) 的一条出边 \((u, v, w)\),然后每条边枚举 \(t\) 并直接入队。
在枚举这么多条边时,会大量出现同一个点入队的情况,这之间 \(dis\) 更大的显然更劣,没有必要入队。
于是先离线(离的是枚举边的线),最后对每个 \(t\) 取最优的入队,分析插入量:\(O(n^2)\)(每个点插入 \(O(n)\) 次),时间复杂度 \(O(n ^ 3 + nm + n^2 \log (n^2))\)

代码

link

const int N = 505;
const ll inf = 0x3f3f3f3f3f3f3f3f;
int n, m;
vector<pair<ll, int>> e[N];
ll len[N][N];
ll dis[N];
void floyd(){rep(k, 1, n){rep(i, 1, n){rep(j, 1, n){chkmn(len[i][j], len[i][k] + len[k][j]);}}}
}
void dijikstra(){priority_queue<pair<ll, int>, vector< pair<ll, int> >, greater< pair<ll, int> >> q;memset(dis, 0x3f, sizeof(dis));dis[1] = 0;q.push({dis[1], 1});while(!q.empty()){auto [dis_u, u] = q.top(); q.pop();if(dis_u > dis[u]) continue;vector<ll> tmp(n + 1, inf);for(auto [v, w] : e[u]){rep(t, 1, n){chkmn(tmp[t], 1ll * (len[v][t] + 1) * w + dis_u);}}rep(i, 1, n){if(dis[i] > tmp[i]){q.push({dis[i] = tmp[i], i});}}}
}
void solve_test_case(){n = read(), m = read();memset(len, 0x3f, sizeof(len));rep(i, 1, n) len[i][i] = 0;rep(i, 1, m){int u = read(), v = read(), w = read();e[u].push_back({v, w});e[v].push_back({u, w});len[u][v] = 1;len[v][u] = 1;}floyd();dijikstra();write(dis[n]);
}
http://www.agseo.cn/news/274/

相关文章:

  • NOIP2024 退役记
  • LG11311
  • CF1746F
  • ABC389F
  • LG10641
  • P11068
  • scp拷贝文件报错
  • ABC150 C-F
  • 【游戏设计】五子棋设计思路
  • LG10516
  • 11.1 定义类和对象
  • C#.NET EFCore.BulkExtensions 扩展详解
  • 2025AI赋能HR新纪元,中国AI HR主流厂商大盘点
  • Linux作业及状态转换
  • C++小白修仙记_LeetCode刷题_队列
  • 设备驱动程序和设备独立性软件的区别
  • Fastjson 1.2.47 远程代码执行
  • 树状数组板子
  • 私有化部署Dify构建企业AI平台教程
  • 树状数组板子2
  • 网络流——OI复健
  • 2025“钉耙编程”中国大学生算法设计暑期联赛(3)
  • Symfony学习笔记 - Symfony Documentation - Getting Started(下)
  • MySQL事务
  • 线段树板子
  • 双列圆锥滚子轴承载荷分布计算程序
  • NOIP 集训日记
  • 矢量篇 - KMLKMZ转SHP
  • js空值合并运算符?? - jerry
  • 记录---让网页像现实世界一样“拿起来,放进去”