zoukankan      html  css  js  c++  java
  • 【图论】最短路

    最短路最常用的算法有:

       单源最短路:      Bellman-Ford 算法,Dijkstra 算法,SPFA 算法。

       任意两点间最短路:Floyd算法。

    Bellman-Ford 可以处理有负边的情况,也可以处理负圈。最多进行V - 1次迭代操作,如果第V次还进行更新操作,说明存在负圈。

    Dijkstra 不仅不能处理负圈,连有负边都不能。

    Floyd 也可以处理负边的情况。判断负圈只需检查最dp[i][i]是否存在负数的顶点i即可。

    SPFA 可以处理负边的情况,是Bellman-Ford的一种队列实现。判断负圈只需判断顶点入队列次数,超过V次就存在负圈。

    Bellman-Ford 算法:时间复杂度:O(VE)

    模板:

    const int INF = 0x3f3f3f3f;
    int d[MAX_V];//最大顶点数,设置为题目中顶点数的最大值
    int V, E;//顶点数;边数
    struct Edge{
    	int from;
    	int to;
    	int cost;
         Edge(int _from = 0, int _to = 0, int _cost = 0) : from(_from), to(_to), cost(_cost){}  } Edge es[MAX_E]; bool bellman_ford(int s){//true:有负圈 false:没有负圈 for(int i = 1; i <= V; ++i){//定点编号从1开始 d[i] = INF; } d[s] = 0;//d[i]为从s到i的最短路 for(int i = 1; i < V; ++i){//最多做V-1次 bool flag = false; for(int j = 0; j < E; ++j){ Edge e = es[i]; if(d[e.to] > d[e.from] + e.cost){ d[e.to] = d[e.from] + e.cost; flag = true; } } if(!flag) return false; } for(int i = 0; i < E; ++i){ Edge e = es[i]; if(d[e.to] > d[e.from] + e.cost) return true; } return false; }

      

    Dijkstra 算法:O(V2)或O(ElogV)

    未优化:O(V2)

    const int INF = 0x3f3f3f3f;
    int cost[MAX_V][MAX_V];
    bool used[MAX_V];//使用过的顶点
    int d[MAX_V];//最大顶点数,设置为题目中顶点数的最大值
    int prev[MAX_V];//最短路上的前驱顶点
    int V;//顶点数
    
    void dijkstra(int s){
    	for(int i = 0; i < V; ++i){
    		d[i] = INF;
    		used[i] = false;
    		pre[i] = -1;
    	}
    	d[s] = 0;
    	for(int i = 1; i < V; ++i){//最多走
    		int k = -1;
    		for(int j = 0; j < V; ++j){
    			if(!used[j] && d[j] < d[k]){
    				k = j;
    } } if(k == -1) break; used[k] = true; for(int j = 0; j < V; ++j){ if(d[j] > d[k] + cost[k][j]){ d[j] = d[k] + cost[k][j]; prev[j] = k; } } } }

      

     堆优化:O(ElogV)

    const int INF = 0x3f3f3f3f;
    int d[MAX_V];//最大顶点数,设置为题目中顶点数的最大值
    int V;//顶点数
    struct Edge{
    	int to;
    	int cost;
    	Edge(int _v = 0, int _cost = 0) : v(_v), cost(_cost){}
    }
    typedef pair<int, int> P;
    vector<Edge> G[MAX_V];
    
    void dijkstra(int s){
    	priority_queue<P, vector<P>, greater<P>> que;
    	for(int i = 0; i < V; ++i){
    		d[i] = INF;
    	}
    	d[s] = 0;
    	que.push(P(0, s));
    	while(!que.empty()){
    		P p = que.top();
    		que.pop();
    		int v = p.second;
    		if(d[v] < p.first) continue;
    		for(int i = 0; i < G[v].size(); ++i){
    			Edge e = G[v][i];
    			if(d[e.to] > d[v] + e.cost){
    d[e.to] = d[v] + e.cost; que.push(P(d[e.to], e.to)); } } } } void addEdge(int s, int t, int cost){ G[s].push_back(Edge(t, cost)); }

      

    Floyd 算法:O(V3)

      Floyd实质上为dp问题。假设dp[i][j]为i与j之间的最短路,然后依次取集合中剩余点k,比较dp[i][j]和dp[i][k] + dp[k][j],即最短路要么不经过点k,要么经过点k。得到递推式:dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j])。这是一个O(n3)时间复杂度的算法。

    模板:

    const int INF = 0x3f3f3f3f;//巧妙设置无穷大值
    int dp[MAX_V][MAX_V];//邻接矩阵表 int V;//顶点数 void floyd(){ for(int k = 0; k < V; ++k){ for(int i = 0; i < V; ++i){ for(int j = 0; j < V; ++j){ dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]); } } } }

      

    SPFA 算法:O(KE) K为所有顶点进入队列的平均次数,一般小于等于2

    const int INF = 0x3f3f3f3f;
    struct Edge{
    	int to;
    	int cost;
    	Edge(int _v = 0, int _cost = 0) : v(_v), cost(_cost){}
    }
    vector<Edge> G[MAX_V];
    int d[MAX_V];//最大顶点数,设置为题目中顶点数的最大值
    bool used[MAX_V];
    int cnt[MAX_V];
    int pre[MAX_V]; int V;//顶点数 bool SPFA(int s){ for(int i = 0; i < V; ++i){ d[i] = INF; cnt[i] = 0; used[i] = false;
    pre[i] = -1; } d[s] = 0; cnt[i] = 1;
    pre[i] = s; used[s] = true; queue<int> que; que.push(s); while(!que.empty()){ int v = que.front(); que.pop(); used[v] = false; for(int i = 0; i < G[v].size(); ++i){ Edge e = G[v][i]; if(d[e.to] > d[v] + e.cost){ d[e.to] = d[v] + e.cost;
    pre[e.to] = v; if(!used[v]){ used[v] = true; que.push(v); if(++cnt[v] >= V)//入队列次数超过顶点数,则存在负圈 return true; } } } } return false; } void addEdge(int s, int t, int cost){ G[s].push_back(Edge(t, cost)); }

      

  • 相关阅读:
    任务调度~Quartz.net实现简单的任务调试
    编译器错误~写JS还是谨慎点好
    编译器错误~不能向ObjectStateManager添加相同的键
    EF架构~将数据库注释添加导入到模型实体类中
    c++ pair类型
    Adobe dreamweaver 5.5安装过程
    c++函数作为参数传递
    c++ vector.clear()
    动态规划之装配线调度问题
    转:VS后缀名详解
  • 原文地址:https://www.cnblogs.com/Atanisi/p/7648197.html
Copyright © 2011-2022 走看看