最短路之dijkstra

觉得这篇博文讲的很不错:Click here

下面的代码可直接用来AC这道题目:hdu-畅通工程续(click here)

One(邻接矩阵 时间复杂度o(n^2))

一个用邻接矩阵存图的模板。邻接矩阵存图主要用于点比较少的(5000个点还是可以的),边比较多的情况。由于二位数组不能开过于大,所以这也是邻接矩阵存图的限制吧!

djk求单源最短路,邻接矩阵存图 

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 5005;
const int INF = 0x3f3f3f3f;

int n, m;
int mmp[MAXN][MAXN];//邻接矩阵存图
bool vis[MAXN];//对点进行标记
int dis[MAXN];//从起点到各个点的最短距离

void init(int n)//对图进行初始化
{
    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(i==j) mmp[i][j] = 0;
            else mmp[i][j] = mmp[j][i] = INF;
        }
    }
}
void getmap(int m)//建图
{
    int u, v, val;//u->v,边权为val
    for(int i=1; i<=m; i++)
    {
        scanf("%d %d %d",&u,&v,&val);
        u++; v++;//可针对不同的题目进行修改,本人比较喜欢从1开始编号
        mmp[u][v] = mmp[v][u] = min(mmp[u][v],val);//无向图(取最小值的原因是若u->v存在多条路,应保存最小的那一条)
       // mmp[u][v] = min(mmp[u][v],val);//有向图
    }
}
void djk(int st, int ed)//起点为st,终点为ed;
{
    for(int i=1; i<=n; i++)
    {
        vis[i] = false;
        dis[i] = mmp[st][i];
    }
    vis[st] = true;//将起点放入vis数组中
    for(int i=1; i<=n-1; i++)//对除了起点之外的边进行遍历,即一共遍历嗯n-1次,为的是将它依次放入vis数组中
    {
        int mn = INF, id = -1;
        for(int j=1; j<=n; j++)
        {
            if(!vis[j] && dis[j]<mn)
            {
                id = j;//记录下该点
                mn = dis[j];
            }
        }
        if(id==-1) break;//若找不到,可以直接退出循环
        vis[id] = true;//将新取出的点放入vis
        for(int j=1; j<=n; j++)
        {
            if(!vis[j] && mmp[id][j] != INF)//对没被放入vis数组的点和存在id->j这条边的点进行更新
            {
                if(dis[j] > mn+mmp[id][j]) //脑海中浮现一个三角形
                {
                    dis[j] = mn+mmp[id][j];
                }
            }
        }
    }
    if(dis[ed]!=INF) cout<< dis[ed] <<endl;
    else cout << "-1" <<endl;
}
int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        init(n);
        getmap(m);
        int x, y;
        cin>> x >> y;
        x++; y++;//切记要看清题目中的城市编号。
        djk(x,y);
    }

    return 0;
}

Two(链式前向星(优先队列优化) 时间复杂度 O(E * log(V)) )

思想和上面是一样的,只不过是存图方式改变了而已。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
#define CLR(a,b) memset(a,b,sizeof(a));
const int N = (int)1e5+10; //最大点数
const int M = (int)1e6+10; //最大边数
const int INF = (int)0x3f3f3f3f;
int n, m; //n是图中点的数目,m是图中边的数目
int head[N], top;
bool vis[N];
int dis[N]; //保存结果
struct Edge //边的定义
{
    int to, val, next;
    Edge(){}
    Edge(int _to, int _val, int _next)
    {
        to = _to; val = _val; next = _next;
    }
}edge[M << 1]; //如果是双向图的的话,边的数量是题目中描述的二倍
void init()
{
    CLR(head,-1);
    top = 0;
}
void Add(int u, int v, int val) //加单向边
{
    edge[top] = Edge(v,val,head[u]);
    head[u] = top++;
}
void getmap(int m)
{
    int u, v, val;
    while(m--)
    {
        scanf("%d %d %d",&u,&v,&val);
        u++; v++;
        Add(u, v, val);
        Add(v, u, val); //如果是双向图,需要加上这个代码
    }
}
void dijkstra(int st, int ed)
{
    CLR(vis,false);
    CLR(dis,INF); //初始化dis数组
    priority_queue<pii, vector<pii>, greater<pii> > qu;///定义pii类型的优先队列
    dis[st] = 0;
    qu.push(make_pair(0,st)); //make_pair(dis, v) 表示v到起点的距离为dis
    while(!qu.empty())
    {
        pii p = qu.top(); //每次离起点最近的
        qu.pop();
        int v = p.second;
        if(vis[v]) continue; //如果该点已经访问过,则跳过
        vis[v] = true; 
        for(int i=head[v]; ~i; i=edge[i].next)//构建一个三角形。看是否能够通过该点使距离缩短
        {
            Edge e = edge[i];
            if(dis[e.to] > dis[v] + e.val)
            {
                dis[e.to] = dis[v] + e.val;
                qu.push(make_pair(dis[e.to],e.to)); //更新e.to 到 起点的距离
            }
        }
    }
    printf("%d\n", dis[ed]==INF ? -1 : dis[ed]);
}
int main()      
{
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    while(~scanf("%d %d",&n,&m))
    {
        init();
        getmap(m);
        int x, y;
        scanf("%d %d",&x,&y);
        x++; y++;
        dijkstra(x,y);
    }
    return 0;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值