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


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

    题目链接:luogu P6880

    题目大意

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

    思路

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

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

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

    然后时间会有点紧,自己卡一卡,搞点 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;
    }
    
  • 相关阅读:
    修复 Visual Studio Error “No exports were found that match the constraint”
    RabbitMQ Config
    Entity Framework Extended Library
    Navisworks API 简单二次开发 (自定义工具条)
    NavisWorks Api 简单使用与Gantt
    SQL SERVER 竖表变成横表
    SQL SERVER 多数据导入
    Devexpress GridControl.Export
    mongo DB for C#
    Devexress XPO xpPageSelector 使用
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P6880.html
Copyright © 2020-2023  润新知