最短路算法模板

最短路算法 - Floyed

  1. 适合于有向图和无向图,并且是解决权值不全为负的最短路问题
  2. 利用动态规划解决任意两点间的最短路径的算法
  3. 时间复杂度是O(n^3),时间复杂度比较高,不适合计算大数据

相关运用:

  • 如果是一个没有边权的图,把相连的两点间的距离设为dis[i][j]=1,不相连的两点设为无穷大,用floyd算法可以判断i j两点是否相连。
  • 如果dis[i][i] != 0,说明此时存在环。
  • 如果利用floyd求最小值的时候,初始化dis为INF , 如果是求最大值的时候初始化为-1。

模板:

void init(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mat[i][j] = mat[j][i] = INF;//根据情况而定
    return;
}
void Floyed(int n)
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(mat[i][j]> mat[i][k] +mat[k][j])
                    mat[i][j] = mat[i][k] + mat[k][j];
    return;
}

需要注意的地方:
①:使用前先将mat[N][N]根据具体情况初始化
②:注意三重循环的顺序,依次是 k->i->j


最短路算法 - Dijkstra

  1. 适合于有向图和无向图,并且是解决权值均非负的最短路问题
  2. 解决单源最短路径问题,用于计算一个节点到其他所有节点的最短路径
  3. 效率优于Floyed

模板:

void Dijstra(int n)
{
    int dis[N];//记录到源点的最短距离
    bool vis[N];//标记是否已经确定了最短距离
    memset(vis,true,sizeof(vis));
    for(int i=1;i<=n;i++) dis[i] = mat[1][i];
    dis[1]=0,vis[1]=false;
    int pos = 1;
    for(int i=1;i<=n;i++)
    {
        int mis = INF;
        for(int j=1;j<=n;j++)//确定加入源点集合的点
        {
            if( vis[j] && dis[j]< mis)
                mis=dis[j],pos=j;
        }
        vis[pos] = false;
        for(int j=1;j<=n;j++)//松弛操作
        {
            if( vis[j] && dis[j] > dis[pos] + mat[pos][j])
                dis[j] = dis[pos] + mat[pos][j];
        }
    }
    printf("%d\n",dis[n]);
}

算法步骤:

a.初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即:U={其余顶点},若v与U中顶点u有边,则 < u,v> 正常有权值,若u不是v的出边邻接点,则< u,v>权值为∞。

b.从U中选取一个距离v最小的顶点k,把k,加入S中(该选定的距离就是v到k的最短路径长度)。

c.以k为新考虑的中间点,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值的顶点k的距离加上边上的权。

d.重复步骤b和c直到所有顶点都包含在S中。

如图所示:
此图片转载自他人
(图片转自他人)


最短路算法 - spfa

*spfa的算法思想(动态逼近法):*
设立一个先进先出的队列q用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。
松弛操作的原理是著名的定理:“三角形两边之和大于第三边”,在信息学中我们叫它三角不等式。所谓对结点i,j进行松弛,就是判定是否dis[j]>dis[i]+w[i,j],如果该式成立则将dis[j]减小到dis[i]+w[i,j],否则不动。
算法步骤:

  • 读取队头顶点u,并将队头顶点u出队(记得消除标记);
  • 将与点u相连的所有点v进行松弛操作,如果能更新估计值(即令d[v]变小),那么就更新,另外,如果点v没有在队列中,那么要将点v入队(记得标记),如果已经在队列中了,那么就不用入队;
  • 以此循环,直到队空为止就完成了单源最短路的求解。

这里写图片描述

贴一下最短路的两种方法的代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 100+10;
const int INF = 1<<27;
int mat[N][N];
void init(int n)
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mat[i][j] = mat[j][i] = INF;
    return;
}
void Floyed(int n)
{
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(mat[i][j]> mat[i][k] +mat[k][j] && i!=j)
                    mat[j][i] = mat[i][j] = mat[i][k] + mat[k][j];

    printf("%d\n",mat[1][n]);
    return;
}
void Dijstra(int n)
{
    int dis[N];
    bool vis[N];
    memset(vis,true,sizeof(vis));
    for(int i=1;i<=n;i++) dis[i] = mat[1][i];
    dis[1]=0,vis[1]=false;
    int pos = 1;
    for(int i=1;i<=n;i++)
    {
        int mis = INF;
        for(int j=1;j<=n;j++)
        {
            if( vis[j] && dis[j]< mis)
                mis=dis[j],pos=j;
        }
        vis[pos] = false;
        for(int j=1;j<=n;j++)
        {
            if( vis[j] && dis[j] > dis[pos] + mat[pos][j])
                dis[j] = dis[pos] + mat[pos][j];
        }
    }
    printf("%d\n",dis[n]);
}
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m) && n && m)
    {
        init(n);
        while(m--)
        {
            int st,ed,w;
            scanf("%d%d%d",&st,&ed,&w);
            if(mat[st][ed]>w) 
                mat[st][ed]  = mat[ed][st] = w;
        }
        Floyed(n);
        Dijstra(n);
    }
    return 0;
}
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
#include <map>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
typedef long long LL;

const int N = 2000+100;
const int INF = 0x3f3f3f3f;
int dis[N];
bool vis[N];
queue<int> que;
struct Node
{
    int next,to,wei;
    Node(){}
    Node(int a,int b,int c) :next(a),to(b),wei(c){}
};
int head[N];struct Node edge[N];int nedge;
void add_edge(int a,int b,int c )
{
    edge[++nedge] = Node(head[a],b,c);
    head[a] = nedge;
    edge[++nedge] = Node(head[b],a,c);
    head[b] = nedge; 
}

void Init()
{

    memset(vis,false,sizeof(vis));
    memset(dis,INF,sizeof(dis));
    while(!que.empty()) que.pop();
}
int spfa(int s,int d)
{
    dis[s] = 0;
    que.push(s);
    vis[s] = true;
    while(!que.empty())
    {
        int now = que.front();
        que.pop();
        vis[now] = false;
        for(int i=head[now];~i;i=edge[i].next)
        {
            int v = edge[i].to;
            if(dis[now] + edge[i].wei < dis[v])
            {
                dis[v] = dis[now] + edge[i].wei;
                if(!vis[v]) 
                {
                    que.push(v);
                    vis[v] = true;
                }
            }
        }
    }
    return dis[d];
}
int main()
{
    int t,s,d;
    while(scanf("%d %d %d",&t,&s,&d)!=EOF)
    {
        memset(head,-1,sizeof(head));
        nedge = -1;
        for(int i=1;i<=t;i++)
        {
            int x,y,w;
            scanf("%d %d %d",&x,&y,&w);
            add_edge(x,y,w);
        }
        int ss[N];
        for(int i=1;i<=s;i++)
            scanf("%d",&ss[i]);
        int ans = INF;
        for(int i=1;i<=d;i++)
        {
            int now;
            scanf("%d",&now);
            for(int i=1;i<=s;i++) 
                Init(),ans = min(ans,spfa(ss[i],now));
        }
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值