Dijkstra

适用范围:

不含负权值边的图


 

基本代码

思路:从点出发,循环至多N次。每一次在当前已知可达路径中选 到达取路径长度 最小且未被访问过得点作为这一次的起点,再遍历该点的邻接点,更新到达路径长度。

//邻接矩阵方式
#include <bits/stdc++.h> using namespace std; const int maxn = 3000 + 10; const int INF = 0x3f3f3f3f; int n, m, st; //n:顶点数 m:边数 st:源点 int dist[maxn]; //dist[i]:从st到i的当前最短路径长度 int path[maxn]; //path[i]:st到i的最短路径上的前一个点 int g[maxn][maxn]; bool visited[maxn]; //顶点i是否被访问过 void Dijsktra() { //初始化 vis dist path memset(visited, false, sizeof(visited)); for(int i = 0; i < n; i++) dist[i] = INF; dist[st] = 0; path[st] = -1; //循环n次 for(int k = 0; k < n; k++){ //1 先找到dist中最小的 且i未被访问的顶点 记录下来作为出发点 int Min = INF; int pos; for(int i = 0; i < n; i++){ if(!visited[i] && dist[i] < Min){ Min = dist[i]; pos = i; } } //2 再以该点为出发点 访问其邻接点 计算和更新 visited[pos] = true; for(int i = 0; i < n; i++){ if(!visited[i] && g[pos][i] != INF && (Min + g[pos][i] < dist[i])){ dist[i] = Min + g[pos][i]; path[i] = pos; //记录来自哪一个顶点 } } } } void showPath(int ed) { //一直向前寻找直到结束 int i = ed; printf("%d ",ed); while(path[i] != -1){ printf("%d ",path[i]); i = path[i]; } printf("\t-->dis : %d\n",dist[ed]); } int main() { while(~scanf("%d%d%d", &n, &m, &st)){ //初始化图 for(int i = 0; i < n; i++){ for(int j = 0; j < n; j++){ g[i][j] = INF; } g[i][i] = 0; } //构建图 for(int i = 0; i < m; i++){ int a, b, c; scanf("%d%d%d",&a, &b, &c); g[a][b] = c; } Dijsktra(); for(int i = 0; i < n; i++){ printf("%d-->%d : ", st, i); showPath(i); } } return 0; }

 

//邻接矩阵方式
#include <bits/stdc++.h>
using namespace std;
const int maxn = 3000 + 10;
const int INF = 0x3f3f3f3f;

struct edge{
    int to, cost;
};
typedef pair<int, int> P;

int n, m;
int d[maxn];
int path[maxn];
bool visited[maxn];
vector<edge> G[maxn];

void dijsktra(int s, int V)
{
    priority_queue<P, vector<P>, greater<P> > que;
    for(int i = 0; i < V; i++) d[i] = INF;
    memset(visited, 0, sizeof(visited));
    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));
            }
        }
    }
}

int main()
{
    int st;
    while(~scanf("%d%d%d", &n, &m, &st)){
        for(int i = 0; i < n; i++) G[i].clear();
        for(int i = 0; i < m; i++){
            edge e; int a;
            scanf("%d%d%d",&a, &e.to, &e.cost);
            G[a].push_back(e);
        }
        dijsktra(st, n);
        for(int i = 0; i < n; i++) printf("%d ",d[i]);
        printf("\n");
    }
    return 0;
}

 


 

拓展1:边上增加花费属性,求最小花费最短路(路径记录则同上)

//修改第二个For循环
//value[i] 到顶点i的所有边的总花费 初始值为INF
//cost[i][j] 边i-j的花费
for(int i = 0; i < n; i++){
    if(!visited[i] && g[pos][i] != INF){
        if(Min + g[pos][i] < dist[i]){
            dist[i] = dist[i] + g[pos][i];
            value[i] = value[pos] + cost[u][v];
        }else if(Min + g[pos][i] == dist[i] && value[pos] + cost[pos][i] < value[i]){
            value[i] = value[pos] + cost[pos][i];
        }
    }
}

 


 

拓展2:顶点上增加花费属性,求最小花费最短路(同拓展一,仅修改cost为一维)

//修改第二个For循环
//value[i] 到顶点i的所有经过顶点的总花费(包括点i) 初始值为INF
//cost[i] 顶点i的花费
for(int i = 0; i < n; i++){
    if(!visited[i] && g[pos][i] != INF){
        if(Min + g[pos][i] < dist[i]){
            dist[i] = dist[i] + g[pos][i];
            value[i] = value[pos] + cost[i];
        }else if(Min + g[pos][i] == dist[i] && value[pos] + cost[i] < value[i]){
            value[i] = value[pos] + cost[i];
        }
    }
}

 


 

拓展3:求最短路径条数

//修改第二个For循环
//num[i] = 0, num[st] = 1  num[i]从st到i的最短路径条数
for(int i = 0; i < n; i++){
    if(!visited[i] && g[pos][i] != INF){
        if(Min + g[pos][i] < dist[i]){
            dist[i] = dist[i] + g[pos][i];
            num[i] = num[pos];
        }else if(Min + g[pos][i] == dist[i]){
            num[i] = num[i] + num[pos];
        }
    }
}

 


 

综合以上拓展中2~3个 例如:求顶点花费最大的最短路路径和其个数。

//修改第二个For循环
for(int i = 0; i < n; i++){
    if(!visited[i] && g[pos][i] != INF){
        if(Min + g[pos][i] < dist[i]){
            dist[i] = dist[i] + g[pos][i]; //更新路径长度
            value[i] = value[pos] + cost[i]; //更新顶点花费
            num[i] = num[pos]; //统计数量
            path[i] = pos; //记录路径
        }else if(Min + g[pos][i] == dist[i]){
            num[i] = num[i] + num[pos];
            if(value[i] < value[pos] + cost[i]){
                value[i] = value[pos] + cost[i];
                path[i] = pos;
            }
        }
    }
}

 


 待理解。。。

用vector把dijkstra的path记录下来(包括)

//path[i]:顶点i可以来自的最短路来源集合
vector<int> path[maxn];
// ...
//修改第二个For循环
if(Min + g[pos][i] < dist[i]){
    dist[i] = dist[pos] + g[pos][i];
    path[i].clear();
    path[i].push_back(pos);
} else if(Min + g[pos][i] == dist[i]){
    path[i].push_back(pos);
}

//然后处理

转载于:https://www.cnblogs.com/sourcry/p/10405809.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值