当前位置: 首页 > 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.zskr.cn/news/214.html

相关文章:

  • NOIP2024 退役记
  • LG11311
  • CF1746F
  • C#.NET EFCore.BulkExtensions 扩展详解
  • 2025AI赋能HR新纪元,中国AI HR主流厂商大盘点
  • 私有化部署Dify构建企业AI平台教程
  • 树状数组板子2
  • NOIP 集训日记
  • 记录---让网页像现实世界一样“拿起来,放进去”
  • Ubuntu22.04安装Docker过程记录
  • MySQL多表查询
  • 软件工程导论第一次作业
  • 闲话 25.9.8
  • The 2025 ICPC Asia East Continent Online Contest (I)
  • Ubuntu22.04下Docker的安装Docker镜像源问题解决方法
  • 【项目实战】基于Hi3861的鸿蒙智能小车(循迹、超声波避障、远程控制、语音控制、4G定位)有教程代码
  • 【项目实战】基于Hi3861的鸿蒙智能小车(循迹、超声波避障、远程控制、语音控制、4G定位)有教程代码
  • 新手小白如何快速入门PostgreSQL
  • Linux Strace 系统调用工具详解与企业应用
  • 想进大厂?从学习圈子里的“管理术语”开始
  • 配电网二进制粒子群重构(BPSO)
  • Agisoft Metashape Professional 2.2.2.21069 多视点三维建模设计
  • 二分查找
  • html中的latex数据公式展示
  • 深度学习入门基于python
  • 图像配准尝试
  • TypeScript索引访问类型详解
  • 安全不是一个功能-而是一个地基
  • 你的错误处理一团糟-是时候修复它了-️
  • 你的测试又慢又不可靠-因为你测错了东西