【HDU/算法】最短路问题 杭电OJ 2544 (Dijkstra,Dijkstra+priority_queue,Floyd,Bellman_ford,SPFA)

最短路径问题是图论中很重要的问题。

解决最短路径几个经典的算法

1、Dijkstra算法

单源最短路径(贪心),还有用 priority_queue 进行优化的 Dijkstra 算法。

2、bellman-ford算法

例题:【ACM】POJ 3259 Wormholes

允许负权边的单源最短路径算法

优点:可以发现负圈。缺点,时间复杂度比Dijkstra算法高。

算法流程:

(1)初始化:将除源点外的所有顶点的最短距离估计值d[v]趋于正无穷,d[start]=0

(2)迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点V中的每个顶点v的最短距离估计值逐步逼近其最短距离(运行|v|-1次)

(3)检验负权回路:判断边集E中的每一条边的两个顶点是否收敛。如果存在未收敛的顶点,则算法返回false,表明问题无解,否则算法返回true,并从源点可达的顶点v的最短距离保存在d[v]中。

Bellman-Ford(G,w,s) :boolean   //图G ,边集 函数 w ,s为源点

1        for each vertex v ∈ V(G) do        //初始化 1阶段

2            d[v] ←+∞

3        d[s] ←0;                             //1阶段结束

4        for i=1 to |v|-1 do               //2阶段开始,双重循环。

5           for each edge(u,v) ∈E(G) do //边集数组要用到,穷举每条边。

6              If d[v]> d[u]+ w(u,v) then      //松弛判断

7                 d[v]=d[u]+w(u,v)               //松弛操作   2阶段结束

8        for each edge(u,v) ∈E(G) do

9            If d[v]> d[u]+ w(u,v) then

10            Exit false

11    Exit true

 

3、SPFA

是bellman-ford + 队列优化,其实和bfs关系更密

4、floyd算法

多元最短路算法,是一个经典的动态规划算法

 

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2544

题目大意是:已知顶点数n,边数及权值,求第1个点到第n个点的最短路的长度

有很多种解法

1、Dijkstra 

推荐博客:【算法】【ACM】深入理解Dijkstra算法(单源最短路径算法)

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
using namespace std;
const int INF = 9999999;
int map[105][105];
int d[105];
bool vis[105];
int N,M;

void dijkstra()
{
    int i,j,min,pos;
    for(i=1;i<N;i++)
    {
        min = INF;
        for(j=1;j<=N;j++)
        {
            if(!vis[j] && d[j]<min)
            {
                min = d[j];
                pos = j;
            }
        }
        if(min == INF)
            return ;
        vis[pos] = true;
        for(j=1;j<=N;j++)
        {
            if(!vis[j] && (min+map[pos][j]<d[j]))
            {
                d[j] = map[pos][j] + min;
            }
        }
    }
}

int main()
{
    int i,j,a,b,t;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        if(N==0 && M==0)
            return 0;
        for(i=0;i<=N;i++)
        {
            for(j=0;j<=N;j++)
            {
                if(i==j)	map[i][j] = 0;
                else	map[i][j] = INF;
            }
        }
        for(i=0;i<M;i++)
        {
            cin >> a >> b >> t;
            if(t < map[a][b])
            {
                map[a][b] = t;
                map[b][a] = t;
            }
        }
        memset(vis,false,sizeof(vis));
        for(i=1;i<=N;i++)
            d[i] = map[1][i];
        vis[1] =true;
        dijkstra();
        cout << d[N] << endl; 
    }
    return 0;
}

2、Floyd 算法

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn = 9999999;
int map[105][105];

int main ()
{
    int n,m;
    int a,b,c;
    int i,j,k;
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)
            return 0;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if(i==j)	map[i][j] = 0;
                else	map[i][j] = maxn;
            }
        }
        for(i=1;i<=m;i++)
        {
            cin >> a >> b >> c;
            if(map[a][b] > c)
            {
                map[a][b] = map[b][a] = c;
            }
        }
        for(k=1;k<=n;k++)
        {
            for(i=1;i<=n;i++)
            {
                for(j=1;j<=n;j++)
                {
                    map[i][j] = min(map[i][j],map[i][k]+map[k][j]);
                }
            }
        }
        cout << map[1][n] << endl;
    }
    return 0;
}

3、Dijkstra + 优先队列 (邻接矩阵表示)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <vector>
#include <functional>
using namespace std;

typedef pair<int,int> p;
const int maxn = 9999999;
int N,M;
int d[105];
bool vis[105];
int map[105][105];

void dijkstra()
{
    int i;
    memset(vis,false,sizeof(vis));
    memset(d,maxn,sizeof(d));
    d[1] = 0;
    priority_queue<p,vector<p>,greater<p> > q;
    q.push(make_pair(d[1],1));
    while(q.empty()!=1)
    {
        p p1=q.top();
        q.pop();
        int pos = p1.second;
        if(vis[pos])
            continue;
        vis[pos] = true;
        for(i=2;i<=N;i++)
        {
            if(!vis[i] && d[i]>map[pos][i]+d[pos])
            {
                d[i] = map[pos][i]+d[pos];
                q.push(make_pair(d[i],i));
            }
        }
    }
}

int main ()
{
    int i,j;
    while(scanf("%d%d",&N,&M)!=EOF)
    {
        if(N==0 && M==0)
            return 0;
        for(i=0;i<=N;i++)
        {
            for(j=0;j<=N;j++)
            {
                if(i==j)    map[i][j] = 0;
                else    map[i][j] = maxn;
            }
        }
        int a,b,c;
        for(i=0;i<M;i++)
        {
            cin >> a >> b >> c;
            if(map[a][b] > c)
            {
                map[a][b] = c;
                map[b][a] = c;
            }
        }
        dijkstra();
        cout << d[N] << endl;
    }
    return 0;
}

4、Dijkstra + 优先队列 + vector(邻接表表示)

#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <cstdio>
#include <functional>
using namespace std;
typedef pair<int,int> p;//first是最短距离,second是顶点编号
const int maxn = 9999999;

struct edge
{
    int to;//邻接的点
    int cost;//以及到该点的权值
};

vector<edge> eg[105];//邻接表
bool vis[105];//表示是否已经使用过
int d[105];//最短距离
int n,m;//顶点数和边数

void dijkstra()
{
    //priority_queue,优先队列按first由小到大,默认的是从大到小
    priority_queue<p,vector<p>,greater<p>> q;
    //初始化
    memset(d,maxn,sizeof(d));
    memset(vis,false,sizeof(vis));
    d[1] = 0;
    q.push(p(d[1],1));
    while(q.empty()!=1)
    {
        p p1 = q.top();
        q.pop();
        int pos = p1.second;
        if(vis[pos])
            continue;
        vis[pos] = true;
        for(int i=0;i<eg[pos].size();i++)
        {
            edge x = eg[pos][i];
            if(d[x.to] > d[pos] + x.cost)
            {
                //起点到x的距离与 选中的这个pos点+pos到x的距离和 作比较
                d[x.to] = d[pos] + x.cost;
                q.push(p(d[x.to],x.to));
            }
        }
    }
}

int main ()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int i;
        if(n==0 && m==0)
            return 0;
        for(i=1;i<=n;i++)
            eg[i].clear();
        int a,b,c;
        for(i=1;i<=m;i++)
        {
            cin >> a >> b >> c;
            edge g1,g2;//无向图的缘故
            g1.to = b;
            g1.cost = c;
            g2.to = a;
            g2.cost = c;
            eg[a].push_back(g1);
            eg[b].push_back(g2);
        }
        dijkstra();
        cout <<d[n] << endl;
    }
    return 0;
}

5、bellman_ford(邻接矩阵)

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 9999999;
int d[105];
int map[105][105];
int n,m;

void bellman_ford()
{
    memset(d,maxn,sizeof(d));
    d[1] = 0;
    int flag = 1;
    int k,i,j;
    for(k=0;k<n-1 && flag;k++)
    {
        flag = 0;
        for(i=1;i<=n;i++)
        {
            for(j=1;j<=n;j++)
            {
                if( map[i][j] && d[i]!=maxn && (d[j]>map[i][j]+d[i]) )
                {
                    d[j] = map[i][j]+d[i];
                    flag = 1;
                }
            }
        }
        if(flag == 0)
            return ;
    }
}

int main()
{
    int i,j;
    int a,b,c;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)
            return 0;
        for(i=0;i<=n;i++)
        {
            for(j=0;j<=n;j++)
            {
                if(i==j)	map[i][j] = 0;
                else	map[i][j] = maxn;
            }
        }
        for(i=1;i<=m;i++)
        {
            cin >> a >> b >> c;
            if(map[a][b] > c)
            {
                map[a][b] = c;
                map[b][a] = c;
            }
        }
        bellman_ford();
        cout << d[n] << endl;
    }
    return 0;
}

用边表示

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 9999999;

typedef struct Edge
{
    int u,v;
    int weight;
}Edge;
Edge edge[10000+10]; 
int d[105];
int n,m;

void bellman_ford()
{
    int flag = 1,i,j;
    for(i=1;i<=n-1 && flag;i++)
    {
        flag = 0;
        //因为是无向图,所以是双向的
        for(j=1;j<=m;j++)
        {
            if(d[edge[j].v]>d[edge[j].u]+edge[j].weight)
            {
                d[edge[j].v]=d[edge[j].u]+edge[j].weight;
                flag = 1;
            }
            if(d[edge[j].u]>d[edge[j].v]+edge[j].weight)
            {
                d[edge[j].u]=d[edge[j].v]+edge[j].weight;
                flag = 1;
            }
        }
        if(!flag)
            return ;
    }
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)
            return 0;
        memset(d,maxn,sizeof(d));
        d[1] = 0;
        for(int i=1;i<=m;i++)
        {
            cin >> edge[i].u >> edge[i].v >> edge[i].weight;
            //注意这里设置初始情况
            if(edge[i].u==1)
            {
                d[edge[i].v] = edge[i].weight;
            }
        }
        bellman_ford();
        cout << d[n] << endl;
    }
    return 0;
}

6、SPFA(邻接矩阵)因为这个题目不存在负权边,所以就没有记录每个点的入队次数

#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 9999999;
int n,m;
int map[105][105];
int d[105];
int vis[105];

void SPFA()
{
    memset(vis,false,sizeof(vis));
    memset(d,maxn,sizeof(d));
    queue<int> Q;
    Q.push(1);
    d[1] = 0;
    vis[1] = true;
    int v;
    while(Q.empty()!=1)
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(v=1;v<=n;v++)
        {
            if(map[u][v]!=maxn)
            {
                if(d[u]+map[u][v]<d[v])
                {
                    d[v] = map[u][v]+d[u];
                    if(!vis[v])
                    {
                        Q.push(v);
                        vis[v] = true;
                    }
                }
            }
        }
    }
}

int main ()
{
    int i,j;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)
            return 0;
        for(i=0;i<=n;i++)
        {
            for(j=0;j<=n;j++)
            {
                if(i==j)	map[i][j] = 0;
                else	map[i][j] = maxn;
            }
        }
        int a,b,c;
        for(i=1;i<=m;i++)
        {
            cin >> a >> b >> c;
            if(map[a][b]>c)
            {
                map[a][b] = c;
                map[b][a] = c;
            }
        }
        SPFA();
        cout << d[n] << endl;
    }
    return 0;
}

用边来表示

#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;

struct Edge
{
    int s,e,w;
    Edge()
    {
        s = -1;
        e = -1;
        w = -1;
    }
};

const int maxn = 9999999;
Edge edge[10000+10];
int n,m;
int d[105];
int vis[105];
int all_e;

void SPFA()
{
    memset(vis,0,sizeof(vis));
    memset(d,maxn,sizeof(d));
    d[1] = 0;
    vis[1] = true;
    queue<int> Q;
    Q.push(1);
    int v;
    while(Q.empty()!=1)
    {
        int u = Q.front();
        Q.pop();
        vis[u] = false;
        for(v=0;v<all_e;v++)
        {
            if(d[edge[v].e]>d[edge[v].s]+edge[v].w)
            {
                d[edge[v].e]=d[edge[v].s]+edge[v].w;
                if(!vis[edge[v].e])
                {
                    Q.push(edge[v].e);
                    vis[edge[v].e] = true;
                }
            }
        }
    }
}

int main ()
{
    int i;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0 && m==0)
            return 0;
        all_e = 0;
        int a,b,c;
        for(i=0;i<m;i++)
        {
            cin >> a >> b >> c;
            edge[all_e].s = a;
            edge[all_e].e = b;
            edge[all_e++].w = c;
            edge[all_e].s = b;
            edge[all_e].e = a;
            edge[all_e++].w = c;
        }
        SPFA();
        cout << d[n] << endl;
    }
    return 0;
}

 

    原文作者:Bellman - ford算法
    原文地址: https://blog.csdn.net/CSDN___CSDN/article/details/86743896
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞