给你一个有向图,你可以至多将一条边的方向反过来,支付翻转这条边的费用。
问你从 1 到 n,再从 n 到 1 的最小费用。如果不行就输出 -1。
Bus / 奥运公交 / オリンピックバス
题目大意
给你一个有向图,你可以至多将一条边的方向反过来,支付翻转这条边的费用。
问你从 1 到 n,再从 n 到 1 的最小费用。如果不行就输出 -1。
思路
我们考虑枚举每条边看是否翻转。
一开始我们先求出从 1 出发和从 n 出发的最短路。
然后我们考虑翻转某条边。
我们把从 1 到 n 和从 n 到 1 分开来看。
如果它不在最短路上(最短路可以跑的时候记录,要用边记录不要用点,不然会超时因为有重边)那它转了之后可能会比最短路优秀。

红色是新的路径,不难看到还要记录反向边以 n 为起点的最短路,以处理 i∼n 的最短路。
同理,你求 n 到 1 的时候还需要反向以 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) {
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() {
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) {
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() {
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);
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__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现