最短路 正在完善

Dijkstra算法


这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径权重被赋为 0 (d[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把d[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于所有顶点的集合 V 中的任意顶点 v, 若 v 不为 s 和上述 m 之一, d[v] = ∞)。当算法结束时,d[v] 中存储的便是从 s 到 v 的最短路径,或者如果路径不存在的话是无穷大。

边的拓展是Dijkstra 算法的基础操作:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(uv)添加到尾部来拓展一条从 s 到 v 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直运行到所有的 d[v] 都代表从 s 到 v 的最短路径的长度值。此算法的组织令 d[u] 达到其最终值时,每条边(uv)都只被拓展一次。

算法维护两个顶点集合 S 和 Q。集合 S 保留所有已知最小 d[v] 值的顶点 v ,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对 u 的每条外接边 (u, v) 进行拓展。

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 1005
#include<algorithm>
using namespace std;
int map1[MAX][MAX];
int ll[MAX],wathll[MAX];
const int INF=0x3f3f3f3f;
int Dijkstra(int n)
{
    int a,b,i,j,k;
    for(i=1; i<=n; i++)
        wathll[i]=map1[1][i];
    ll[1]=1;
    for(j=1; j<n; j++)
    {
        p=-1;
        min1=INF;
        for(i=1; i<=n; i++)
        {
            if(wathll[i]<min1&&(!ll[i]))
            {
                min1=wathll[i];
                p=i;
            }
        }
        if(p==-1)
            break;
        ll[p]=1;
        for(i=1; i<=n; i++)
        {
            if(wathll[p]+map1[p][i]<wathll[i]&&(!ll[i]))
            {
                wathll[i]=wathll[p]+map1[p][i];
            }
        }
    }
    if(j!=n)
        return -1;
    else
        return 1;
}
int main()
{
    int a,b,i,j,k;
    memset(map1,0,sizeof(map1));
    scanf("%d",&a);
    for(i=0;i<a;i++)
        for(j=0;j<a;j++)
            scanf("%d",&map1[i][j]);
    sum=Dijkstra(a);//被调结束后里面存的是到1的最短距离
    printf("%d",sum);
    return 0;
}

Bellman-Ford算法

贝尔曼-福特算法与迪科斯彻算法类似,都以松弛操作为基础,即估计的最短路径值渐渐地被更加准确的值替代,直至得到最优解。在两个算法中,计算时每个边之间的估计距离值都比真实值大,并且被新找到路径的最小长度替代。 然而,迪科斯彻算法以贪心法选取未被处理的具有最小权值的节点,然后对其的出边进行松弛操作;而贝尔曼-福特算法简单地对所有边进行松弛操作,共{\displaystyle |V|-1}{\displaystyle |V|-1}次,其中{\displaystyle |V|}{\displaystyle |V|}是图的点的数量。在重复地计算中,已计算得到正确的距离的边的数量不断增加,直到所有边都计算得到了正确的路径。这样的策略使得贝尔曼-福特算法比迪科斯彻算法适用于更多种类的输入。

贝尔曼-福特算法的最多运行{\displaystyle O(|V|\cdot |E|)}{\displaystyle O(|V|\cdot |E|)}大O符号)次,{\displaystyle |V|}|V|{\displaystyle |E|}|E|分别是节点和边的数量)。

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 1005
#include<algorithm>
#include<vector>
using namespace std;
int map1[MAX][MAX];
int ll[MAX],wathll[MAX];
const int INF=0x3f3f3f3f;
struct node
{
    int end1;
    int power;
}
vector <node> v[MAX];
int Bellman(int a)
{
    int a,b,i,j,k,m,n,l;
    memset(wathll,0x3f3f3f3f,sizeof(wathll));
    for(i=0;i<v[1].size();i++)
    {
        if(wathll[v[1][i].end1]>v[1][i].power)
            wathll[v[1][i].end1]=v[1][i].power;
    }
    for(i=2;i<=a;i++)
    {
        for(j=0;j<v[i].size();j++)
        {
            if(wathll[v[1][i].end1]>v[1][i].power)
                wathll[v[1][i].end1]=v[1][i].power;
        }
    }
}
int main()
{
    int a,b,i,j,k,m,n,b,z;
    memset(map1,0,sizeof(map1));
    node p;
    scanf("%d%d",&a,&b);
    for(i=1;i<=b;i++)
    {
        scanf("%d%d%d",&m,&n,&z);
        p.end1=n;
        p.power=z;
        v[m].push_back(p);
        p.end1=m;
        v[n].push_back(p);
    }
    sum=Bellman(a);//被调结束后里面存的是到1的最短距离
    printf("%d",sum);
    return 0;
}

SPFA算法

本算法没有经过证明的平均时间复杂度。

给定一个有向带权图{\displaystyle G=(V,E)}{\displaystyle G=(V,E)}和一个源点{\displaystyle s}s,SPFA算法计算从{\displaystyle s}s到图中每个节点{\displaystyle v}{\displaystyle v}的最短路径。对于每个节点{\displaystyle v}{\displaystyle v},从{\displaystyle s}s{\displaystyle v}{\displaystyle v} 的最短路径表示为{\displaystyle d(v)}{\displaystyle d(v)}

SPFA算法的基本思路与贝尔曼-福特算法相同,即每个节点都被用作用于松弛其相邻节点的备选节点。相较于贝尔曼-福特算法,SPFA算法的提升在于它并不盲目尝试所有节点,而是维护一个备选节点队列,并且仅有节点被松弛后才会放入队列中。整个流程不断重复直至没有节点可以被松弛。

下面是这个算法的伪代码。[4]这里的{\displaystyle Q}{\displaystyle Q}是一个备选节点的先进先出队列,{\displaystyle w(u,v)}{\displaystyle w(u,v)} 是边{\displaystyle (u,v)}{\displaystyle (u,v)}的权。

#include <iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define MAX 1005
#include<algorithm>
#include<vector>
#include<deque>
#include<stack>
using namespace std;
int map1[MAX][MAX];
int ll[MAX],wathll[MAX],mmm[MAX];
const int INF=0x3f3f3f3f;
struct node
{
    int end1;
    int power;
}
vector <node> v[MAX];
deque<int>q;

deque<int>q;


int SPFA(int s)
{
    int a,b,c,i,j,k;
    wathll[s]=0;
    q.push_back(s);
    ll[s]=1;
    while(!q.empty())
    {
        a=q.front();
        q.pop_front();
        ll[a]=0;
        for(i=0;i<v[a].size();i++)
        {
           b=v[a][i].end1;
           c=v[a][i].power;
           if(wathll[a]+c<wathll[b])
           {
               wathll[b]=wathll[a]+c;
               if(ll[b]==0)
               {
                   q.push_back(b);
                   ll[b]=1;
                   mmm[b]++;
                   if(mmm[b]>s)
                    return -1;
               }
           }
        }
    }
}
int main()
{
    int a,b,i,j,k,m,n,z,sum;
    memset(mmm,0,sizeof(mmm));
    memset(wathll,0x3f3f3f3f,sizeof(wathll));
    node p;
    scanf("%d%d",&a,&b);
    for(i=1;i<=b;i++)
    {
        scanf("%d%d%d",&m,&n,&z);
        p.end1=n;
        p.power=z;
        v[m].push_back(p);
        p.end1=m;
        v[n].push_back(p);
    }
    sum=SPFA(a);//被调结束后里面存的是到a的最短距离
    printf("%d",sum);
    return 0;
}

Floyd-Warshall算法

Floyd-Warshall算法的原理是动态规划[4]

{\displaystyle D_{i,j,k}}D_{​{i,j,k}}为从{\displaystyle i}i{\displaystyle j}j的只以{\displaystyle (1..k)}(1..k)集合中的节点为中间节点的最短路径的长度。

  1. 若最短路径经过点k,则{\displaystyle D_{i,j,k}=D_{i,k,k-1}+D_{k,j,k-1}}D_{​{i,j,k}}=D_{​{i,k,k-1}}+D_{​{k,j,k-1}}
  2. 若最短路径不经过点k,则{\displaystyle D_{i,j,k}=D_{i,j,k-1}}D_{​{i,j,k}}=D_{​{i,j,k-1}}

因此,{\displaystyle D_{i,j,k}={\mbox{min}}(D_{i,j,k-1},D_{i,k,k-1}+D_{k,j,k-1})}D_{​{i,j,k}}={\mbox{min}}(D_{​{i,j,k-1}},D_{​{i,k,k-1}}+D_{​{k,j,k-1}})

在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。



int fulord(int n,int m)//实际上就是遍历过所有点
{
    for(k=1;k<=n;k++)
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
             {
                if(map1[i][k]+map1[k][j]<map1[i][j])
                    map1[i][j]=map1[i][k]+map1[k][j];
            }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值