【模板】Dijkstra迪杰特斯拉算法

【模板】Dijkstra迪杰特斯拉算法

邻接矩阵+朴素版dijkstra->稠密图使用

邻接表+堆优化版dijkstra->稀疏图使用

判断方法:以m=nlogn为界,m越大,图越稠密(参考快速判断一个图是稀疏图的还是稠密图_稠密图和稀疏图界定-CSDN博客

不同类型的图的存储 邻接表 模板
//无权图
int n, m;
vector<vector<int> > adj;

void solve() {
  int a, b;
    
  cin >> n >> m;
  adj.resize(n + 1);

  for (int i = 0; i < m; ++i) {       //建图
        cin>>a>>b;
        adj[a].push_back(b);
        adj[b].push_back(a);//无向图时用
  }

  return;
}


//带权图
int n, m;
vector<vector<pii>> adj; // 有权图 {节点, 边权}

void solve()
{
    int a, b, c;

    cin>>n>>m;
    adj.resize(n+1); //初始化邻接表
    
    for (int i = 0; i < m; ++i) {       //建图
        cin>>a>>b>>c;
        adj[a].push_back(make_pair(b, c));
        adj[b].push_back(make_pair(a, c));//无向图时用
    }

    return;
}

算法运用:
main():测试样例数目
solve():题解
其他详见注释

/*
 *邻接表
 *堆优化版dijkstra(稀疏图时好)
 *时间复杂度 O(mlogn)
 */

#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;


/*—全局的邻接表所需变量--函数----——————————————————————————————————————————————————————————*/
void display_route(int s, int f, vector<int>&pre, vector<int>&path); // 展示最短路径

int n, m;   // 节点数,边数
vector<vector<pii>> adj; // 有向有权图 邻接表


/*———dijkstra——————————————————————————————————————————————————————————————————————————*/
/*
 *[pre path] 在需要存路径时使用
 *[dis vis]  根据需要可选择设为局部变量并传参(是否有多个起点什么的)选择放局部变量还是全局变量
 *若n m未知,则可以用adj.size()或其他什么的size()求得。但此种情况建图时的写法也会不同。
 */
// 求start号点到其他点的最短距离,如果不存在,则(dis[n] == 0x3f3f3f3f)
void dijkstra(int start, vector<int>& dis, vector<int>& pre){
    dis.resize(n+1, INF);
    pre.resize(n+1, -1); //要存路径时用
    vector<int> vis(n+1, 0);

    dis[start] = 0;

    priority_queue<pii, vector<pii>, greater<pii>> pq;   // 小根堆,存储节点编号和距离
    pq.push(make_pair(start, 0));   // first存储节点编号,second存储距离

    while (!pq.empty()) {
        pii p = pq.top();
        pq.pop();

        int u = p.first, d = p.second;

        if(vis[u]) continue;
        vis[u]=1;

        for (int i = 0; i < adj[u].size(); i++) {
            int v = adj[u][i].first, w = adj[u][i].second;
            if (dis[v] > dis[u] + w) {
                dis[v] = dis[u] + w;
                pq.push(make_pair(v, dis[v]));
                pre[v] = u; // 要存路径时用
            }
        }
    }
    return;
}

void solve(){
    int a, b, c;

    cin>>n>>m;
    adj.resize(n+1); //初始化邻接表
    for(int i=0;i<m;i++){      //建图
        cin>>a>>b>>c;
        adj[a].push_back(make_pair(b, c));
        adj[b].push_back(make_pair(a, c));//无向图时用
    }

    //运行算法
    //如果dis只用一次的话,干脆在dijkstra里面,免得传来传去
    vector<int> dis, pre; //pre在要存路径时才用
    dijkstra(1, dis, pre); //1为起点

    //输出路径
    vector<int> path;
    int s, f;//起点,终点
    cin>>s>>f;
    cout<<"dis: "<<dis[f]<<endl;
    display_route(s, f, pre, path);
    
    return;
}

void display_route(int s, int f, vector<int>&pre, vector<int>&path)
{
    cout<<"path: ";
    for(int i=f; i!=-1; i=pre[i]){
        // cout<<i<<" "; // 这样的话是倒序的
        path.push_back(i); // 先倒序存起来
    }

    for(int i=path.size()-1;i>=0;i--){
        cout<<path[i]<<" ";
    }
    cout<<endl;
    return;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0); 
    int T=1;
    // cin>>T;
    while (T--) solve();
    return 0;
}
/*
 *邻接矩阵(优势在于判断是否有边)
 *朴素dijkstra算法(稠密图时好)
 *时间复杂是 O(n2+m), n 表示点数,m 表示边数
 */
#include<bits/stdc++.h>
using namespace std;

typedef pair<int,int> pii;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;

/*—建立邻接矩阵——————————————————————————————————————————————————————————————————————————*/
#define N 100

void CreateUDN(int n, int m); // 建立邻接矩阵
void dijkstra(int s);// s为起始点
// void display_route(int s, int f);// 展示最短路径

int arcs[N][N];//邻接矩阵,权重为整数
int n, m;   // 定点数,边数
// vector<vector<int> > arcs(N, vector<int>(N)); // 用vec也行

// char vex[N]; //顶点表,在顶点为字母时可能会用
// int cost[N][N];// 如果还有额外开销则再加一个矩阵

// 有权w,0(i==j), INF
// 无权把w删掉1,0(i==j或无边)
vector<int> rout[N];
void CreateUDN(int n, int m)
{
    int u, v, w;
    memset(arcs, INF, sizeof(arcs));
    for(int i=0;i<m;i++){
        cin>>u>>v>>w;
        arcs[u][v]=w;
        arcs[v][u]=w; //无向图使用
    }
    return;
}


/*———dijkstra——————————————————————————————————————————————————————————————————————————*/
int dis[N], vis[N];// 距离,是否访问

void dijkstra(int s){
    memset(dis, INF, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    for(int i=1;i<=n;i++){
        dis[i]=arcs[s][i];
    }
    dis[s] = 0;
    vis[s]= 1 ;

    for(int i=1;i<=n;i++){
        rout[i].push_back(s);
    }
    
    for (int i = 1; i <= n; i++){
        int u = 0, mind = 0x3f3f3f3f;
        for (int j = 1; j <= n; j++){
            if (!vis[j] && dis[j] < mind)
                u = j, mind = dis[j];
        }
        vis[u] = 1;
        rout[u].push_back(u);
        // for (auto ed : e[u]) {
        //   int v = ed.v, w = ed.w;
        //   if (dis[v] > dis[u] + w) dis[v] = dis[u] + w;
        // }
        for(int j=1;j<=n;j++){
            /*不用存路径的做法*/
            // dis[j] = min(dis[j], dis[u] + arcs[u][j]);
            /*要存的*/
            if (!vis[j] && dis[j]>dis[u]+arcs[u][j]){
                dis[j]=dis[u]+arcs[u][j];
                rout[j]=rout[u];
            }
        }
    }
    return;
}

void display_route(int s, int f)
{
    cout<<"path: ";
    for(int i=0;i<rout[f].size();i++){
        cout<<rout[f][i]<<" ";
    }
    cout<<endl;
    return;
}

void solve(){
    int a, b, c;

    cin>>n>>m;
    CreateUDN(n, m); //初始化邻接矩阵

    //运行算法
    dijkstra(1); //0为起点

    //输出路径
    int s, f;//起点,终点
    cin>>s>>f;
    cout<<"dis:"<<dis[f]<<endl;
    display_route(s, f);
    
    return;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0); 
    int T=1;
    // cin>>T;
    while (T--) solve();
    return 0;
}

示例数据:
输入

6 9
1 2 7
1 3 9
1 6 14
2 3 10
2 4 15
3 4 11
3 6 2
4 5 6
5 6 9
1 5

输出

dis: 20
path: 1 3 6 5

参考:

Storage - OI Wiki (oi-wiki.org)

迪杰斯特拉(Dijkstra’s )算法——解决带权有向无向图最短路径-CSDN博客

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RIDL_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值