zoukankan      html  css  js  c++  java
  • 最短路回顾

    准备开图论,复习一下基本算法。

    1. 最短路算法

    (V) : 点集

    (E) : 边集

    Floyd

    洛谷模板

    应该从动态规划的角度去考虑这个算法,设 f[i][j] 表示从 ij最短路径 (指某条路,而不是最小距离这样一个数字,距离是路径的一个属性)。

    其实所有的最短路算法都应该落实到路径上

    则很显然,有如下转移

    [f[i][j] = min(f[i][j],f[i][k] + f[k][j]) ]

    这个 (+)理解成路径的拼接,而 min 表示按数值比较

    复杂度分析

    对于每一个f[i][j] 都需要 1 <= k <= n 去更新

    (O(V^3))

    抽象出来

    struct node {
    	vector<int>path;
    	int dis;
    	bool operator < (const node& b)const {
    		return dis < b.dis;
    	}
    	node operator + (node b) {
    		node tmp = *this;
    		tmp.path.insert(tmp.path.end(), b.path.begin(), b.path.end());
    		tmp.dis += b.dis;
    		return tmp;
    	}
    }f[N][N];
    

    实例代码

    /*
     * @Author: zhl
     * @Date: 2020-10-19 18:31:45
     */
    #include<bits/stdc++.h>
    using namespace std;
    const int N = 5e2 + 10;
    
    struct node {
    	vector<int>path;
    	int dis;
    	bool operator < (const node& b)const {
    		return dis < b.dis;
    	}
    	node operator + (node b) {
    		node tmp = *this;
    		tmp.path.insert(tmp.path.end(), b.path.begin(), b.path.end());
    		tmp.dis += b.dis;
    		return tmp;
    	}
    }f[N][N];
    
    int n, m;
    int main() {
    	cin >> n >> m;
    	for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)f[i][j] = node{ vector<int>(),0x3f3f3f3f };
    	for (int i = 1; i <= n; i++)f[i][i] = node{ vector<int>(),0 };
    	for (int i = 1; i <= m; i++) {
    		int a, b, c;
    		cin >> a >> b >> c;
    		//sb题目会有重边
    		if (c < f[a][b].dis)f[a][b] = node{ vector<int>(1,b),c };
    		if (c < f[b][a].dis)f[b][a] = node{ vector<int>(1,a),c };
    	}
    	for (int k = 1; k <= n; k++) {
    		for (int i = 1; i <= n; i++) {
    			for (int j = 1; j <= n; j++) {
    				if (f[i][k] + f[k][j] < f[i][j]) {
    					f[i][j] = f[i][k] + f[k][j];
    				}
    			}
    		}
    	}
    
    	int q;
    	cin >> q;
    	while (q--) {
    		int a, b;
    		cin >> a >> b;
    		cout << "Min_Dis : " << f[a][b].dis << endl;
    		cout << a;
    		for (int i : f[a][b].path)cout << "->" << i;
    		cout << endl;
    	}
    }
    

    做题直接存数值就完事了

    Bellman-ford

    (O(VE))

    用边去进行松弛操作

    Floyd 不同,Bellman-ford 处理的是 ‘单源最短路’ 。处理的是从源点 s 出发到任意一点 t 的最短路径

    对于一条边 E[i] ,该边连接 a , b ,边权是 w

    f[b] = min(f[b],f[a] + w)

    n-1 次,每次都遍历所有的边进行松弛

    再跑一次,如果松弛成功,则说明有负环。

    n 个点的图上的路径最多 n-1 条边

    代码懒得写了(跑

    Dijkstra

    时间复杂度 (O(VlgV)) , 一般正权图用 Dij就完事

    struct Node{
        double d;
        int id;
        bool operator < (const Node& b)const{
            return d > b.d;
        }
    };
    int n,m,s,t;
    void Dijkstra(){
        rep(i,0,n){
            dis[i] = INF;
            vis[i] = 0;
        }
        dis[s] = 0;
        priority_queue<Node>Q;
        Q.push(Node{0,s});
        
        while(!Q.empty()){
            Node tp = Q.top();Q.pop();
            int u = tp.id;
            double d = tp.d;
            if(vis[u])continue;
            vis[u] = 1;
            repE(i,u){
                int v = E[i].to;
                if(dis[v] > d + E[i].x + E[i].y * A){
                    dis[v] = d + E[i].x + E[i].y * A;
                    Q.push(Node{dis[v],v});
                }
            }
        }
    }
    

    三种状态

    1. vis[u] == True ,表示 su 的最短路径已经被处理出来

    2. 在队列中,表示该点目前可达但是不确定是否已经是最短路径

    3. 目前不可达

    每次取队列中距离最短的那个点,给他打上标记。

    因为这个点必定已经是最短路径。

    因为 u是目前所有可达的点中距离最短的一个,若还有另一条更短的路径,则肯定有一条路径 s->tt->us->t 的最短距离必定小于目前 u 的最短路径,且 t 必定会在队列中(这里想的也不是特别清楚,好像不是很严谨)

    需要注意的是,这里的 visBFS 中的标记 vis 不一样

    BFS 中需要入队的时候打上标记,不能取队首的时候打标记,否则会多次入队

    而这里的 vis 表示的是已经处理完毕

    SPFA

    一种对 Bellman-ford 的优化

    很显然 Bellman-ford 中做了很多次无用的松弛,例如第一次的时候只需要松弛和源点相连的边就可以。

    每一个点入队,就表示有一个点的 dis 下降了,而在无负环的图中,必然存在最短路径。所以该算法一定能在有限次的执行后停止。

    复杂度 : (O(kV))(k) 是平均每个点的入队次数

    在极端条件下可以被卡成朴素 bellman-ford (O(EV))

    void spfa() {
    	queue<int> q;
    	memset(dis, 0x3f, sizeof(int) * (n + 10));
    	memset(vis, 0, sizeof(int) * (n + 10));
    
    	q.push(s); dis[s] = 0; vis[s] = 1; //第一个顶点入队,进行标记
    	while (!q.empty()) {
    		int u = q.front();
    		q.pop(); vis[u] = 0;
    		for (int i = head[u]; ~i; i = E[i].next) {
    			int v = E[i].to;
    			if (dis[v] > dis[u] + E[i].w) {
    				dis[v] = dis[u] + E[i].w;
    				if (vis[v] == 0) {
                        //此处判环
                        //if(++cnt[v] >= n) 有负环
    					vis[v] = 1;
    					q.push(v);
    				}
    			}
    		}
    	}
    }
    
  • 相关阅读:
    消灭WinRAR广告
    DLL:操作数据库和表
    MySQL的概述和基础(学习整理)
    MySQL个人用户的安装配置详解
    Maven的几种新建项目方式
    解决Maven的jar包冲突问题
    Maven Web项目出现org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException错误
    Maven的概述和基础(学习整理)
    从Maven中央仓库下载jar包
    Maven的New中没有Servlet问题(IDEA)
  • 原文地址:https://www.cnblogs.com/sduwh/p/13842319.html
Copyright © 2011-2022 走看看