zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    HUD--2553 N皇后问题
    poj--2139
    poj--2236
    poj--2229
    poj--2376 Cleaning Shifts
    poj--3669
    poj--1979 Red and Black
    poj--1258
    经典DP问题--poj1088滑雪
    Poj 1041--欧拉回路
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P6880.html
Copyright © 2011-2022 走看看