【luogu P6880】Bus / 奥运公交 / オリンピックバス(最短路)

Bus / 奥运公交 / オリンピックバス

题目链接:luogu P6880

题目大意

给你一个有向图,你可以至多将一条边的方向反过来,支付翻转这条边的费用。
问你从 1 到 n,再从 n 到 1 的最小费用。如果不行就输出 -1。

思路

我们考虑枚举每条边看是否翻转。
一开始我们先求出从 1 出发和从 n 出发的最短路。
然后我们考虑翻转某条边。

我们把从 1n 和从 n1 分开来看。
如果它不在最短路上(最短路可以跑的时候记录,要用边记录不要用点,不然会超时因为有重边)那它转了之后可能会比最短路优秀。
在这里插入图片描述
红色是新的路径,不难看到还要记录反向边以 n 为起点的最短路,以处理 in 的最短路。
同理,你求 n1 的时候还需要反向以 1 为起点的最短路。

那如果原本在最短路上呢?
那就看起来不太好处理,但是你发现 n 只有 200,说明你最短路上的就 200 级别,是可以每条边都暴力翻转,直接暴力重新跑一次 dij 的。
然后复杂度大概就是 O(n3),就可以过。

然后时间会有点紧,自己卡一卡,搞点 O2,不用跑的 dij 别跑就可以了。
(由于 n 比较小,dij 不加堆优化反而更快?)

代码

#include<queue> #include<cstdio> #include<cstring> #include<iostream> #define ll long long using namespace std; struct node { ll x; int to, nxt; ll cst; bool use; }e[100001]; int n, m, x, y, fr[201], _fr[201], re; int le[201], KK, now; ll dis[201], dis_[201], ans, _dis[201], disd[201], z, zz, nl, nr, tmp[201]; bool in[201]; char c; int read() { re = 0; c = getchar(); while (c < '0' || c > '9') c = getchar(); while (c >= '0' && c <= '9') { re = (re << 3) + (re << 1) + c - '0'; c = getchar(); } return re; } void add(int x, int y, ll z, ll zz) { e[++KK] = (node){z, y, le[x], zz, 1}; le[x] = KK; e[++KK] = (node){z, x, le[y], zz, 0}; le[y] = KK; } void dij1(bool op) {//正向从 1 跑 now = 0; memset(dis, 127 / 3, sizeof(dis)); memset(in, 0, sizeof(in)); dis[1] = 0; for (int i = 1; i <= n; i++) { now = 0; for (int j = 1; j <= n; j++) if (!in[j]) now = (dis[now] < dis[j]) ? now : j; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (e[i].use && dis[e[i].to] > dis[now] + e[i].x) { dis[e[i].to] = dis[now] + e[i].x; if (op) fr[e[i].to] = i; } } } void dij2() {//反向从 1 跑 now = 0; memset(dis_, 127 / 3, sizeof(dis_)); memset(in, 0, sizeof(in)); dis_[1] = 0; for (int i = 1; i <= n; i++) { now = 0; for (int j = 1; j <= n; j++) if (!in[j]) now = (dis_[now] < dis_[j]) ? now : j; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (!e[i].use && dis_[e[i].to] > dis_[now] + e[i].x) { dis_[e[i].to] = dis_[now] + e[i].x; } } } void dij3(bool op) {//正向从 n 跑 now = 0; memset(_dis, 127 / 3, sizeof(_dis)); memset(in, 0, sizeof(in)); _dis[n] = 0; for (int i = 1; i <= n; i++) { now = 0; for (int j = 1; j <= n; j++) if (!in[j]) now = (_dis[now] < _dis[j]) ? now : j; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (e[i].use && _dis[e[i].to] > _dis[now] + e[i].x) { _dis[e[i].to] = _dis[now] + e[i].x; if (op) _fr[e[i].to] = i; } } } void dij4() {//反向从 n 跑 memset(disd, 127 / 3, sizeof(disd)); memset(in, 0, sizeof(in)); disd[n] = 0; for (int i = 1; i <= n; i++) { now = 0; for (int j = 1; j <= n; j++) if (!in[j]) now = (disd[now] < disd[j]) ? now : j; in[now] = 1; for (int i = le[now]; i; i = e[i].nxt) if (!e[i].use && disd[e[i].to] > disd[now] + e[i].x) { disd[e[i].to] = disd[now] + e[i].x; } } } int main() { n = read(); m = read(); for (int i = 1; i <= m; i++) { x = read(); y = read(); z = read(); zz = read(); add(x, y, z, zz); } dij1(1); dij2(); dij3(1); dij4(); ans = dis[n] + _dis[1]; for (int nw = 1; nw <= n; nw++) for (int i = le[nw]; i; i = e[i].nxt) { if (!e[i].use) continue; if (!(fr[e[i].to] == i && dis[nw] + e[i].x + disd[e[i].to] == dis[n])) nl = min(dis[n], e[i].x + dis[e[i].to] + disd[nw]);//不在最短路上,看硬要翻转走会不会比最短路优 else {//在最短路上 e[i].use = 0; e[i + 1].use = 1; for (int i = 1; i <= n; i++) tmp[i] = dis[i]; dij1(0);//直接换边重新跑 dij,看会不会更优 nl = dis[n]; e[i].use = 1; e[i + 1].use = 0; for (int i = 1; i <= n; i++) dis[i] = tmp[i]; } if (!(_fr[e[i].to] == i && _dis[nw] + e[i].x + dis_[e[i].to] == _dis[1])) nr = min(_dis[1], e[i].x + _dis[e[i].to] + dis_[nw]); else {//与上面同理 e[i].use = 0; e[i + 1].use = 1; for (int i = 1; i <= n; i++) tmp[i] = _dis[i]; dij3(0); nr = _dis[1]; e[i].use = 1; e[i + 1].use = 0; for (int i = 1; i <= n; i++) _dis[i] = tmp[i]; } ans = min(ans, nl + nr + e[i].cst); } if (ans >= dis[0]) printf("-1"); else printf("%lld", ans); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P6880.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(34)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示