zoukankan      html  css  js  c++  java
  • 【图论】最短路问题之spfa

    写在算法前面:

    前向星存图(一个神奇的超越邻接矩阵的存在)

    首先讲一下需要定义的一些东西??

    1.head数组:head[点数]:head[i]表示以当前点i为起点的最后一条边(这里的最后指的是编号【我们按输入顺序给边编一个号】)。

    这个图即为head[1]=4,表示以1为起点的边的最后一条是点1—>点5编号为4的边;

    2.num_edge,表示边的总个数;

    3.结构体:

    struct Edge{
        int next,to,dis;
    }edge[边数];

    这里,edge[i].next表示上一条以i为起点的边:

    还是上面那个图,这里edge[4].next=3;

    edge[i].to表示这条边i的终点;

    edge[i].dis表示这条边的权值;

    存图
    void addedge(int from,int to,int dis){
        num_edge++;//因为存入一条边,总边数+1;
        edge[num_edge].next=head[from];//新存入一条以from为起点的边,之前的以from为起点的最后一条边变成了新边的上一条边
        edge[num_edge].to=to;//存终点
        edge[num_edge].dis=dis;//存权值
        head[from]=num_edge;//存入一条以from为起点的边,那么现在以from为起点的最后一条边就是新存入的边
    }

    提醒:如果要存的是无向图,进行程序时应该:addedge(from,to,dis),addedge(to,from,dis)各跑一遍,所开空间也要*2;

    大概是讲完了链式前向星存图;

    spfa

    算法思想:

    1.运用队列的思想,先把起点入队,更新起点能够到达的点,更新这些点到起点的伪最短路(因为可能还有更短情况)然后把得到更新的点中不在队列里的加入队列,以便更新其他点。

    2.当前更新的是伪最短路即可能会有更优情况

    eg:起点是1,1到6距离为7,加入队列dis[6]=7,而1到7距离为2,7到6距离为3,会将dis[6]更新为2+3=5。所以一遍一遍查,最后将会是最短路。

    时间复杂度大约是O(ke),稀疏图中k约等于2,但是毒瘤数据会把复杂度卡成O(nm);

    spfa在存图基础上再开vis[点数]查询某个点是否已经在队列里,避免重复入队(请注意,是是否在队列里 队列里 队列里)

    dis[点数]用来存某个点到起点的最短路;

    以洛谷p3371【模板】单源最短路径(弱化版) 为例纸(标准的过不去qwq)

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    
    using namespace std;
    
    const int inf=2147483647;//题目要求的初始值
    
    queue <int> q;//定义一个队列q
    int n,m,s;
    int head[10010],num;
    int vis[10010],dis[10010];
    
    struct Edge{
        int next,to,dis;
    }edge[500010];
    
    void addedge(int from,int to,int dis){//加边
        num++;
        edge[num].next=head[from];
        edge[num].to=to;
        edge[num].dis=dis;
        head[from]=num;
    }
    
    void spfa(){
        for(int i=1;i<=n;i++){//首先初始化,将所有点的dis赋值为2147483647,所有点都不在队列里,vis为0
            dis[i]=inf;
            vis[i]=0;
        }
        q.push(s);//把起点入队
        dis[s]=0;//起点到起点,最短路为0
        vis[s]=1;//入队了,vis变为1
        while(!q.empty()){//开始循环
            int u=q.front();
            q.pop();//把队首元素出队
            vis[u]=0;//出队了,vis重置为0(因为可能还有更近的下次还能循环到)
            for(int i=head[u];i;i=edge[i].next){//遍历以u为起点的所有遍
                int v=edge[i].to;//v为终点
                if(dis[v]>dis[u]+edge[i].dis){//如果之前的最短路比由u点出发再”拐弯“的点的要大,显然他不是最短,更新他
                    dis[v]=dis[u]+edge[i].dis;
                    if(!vis[v]){//如果终点不在队列里,入队
                        q.push(v);
                        vis[v]=1;
                    }
                }
            }
        }
    }
    
    int main(){
        scanf("%d%d%d",&n,&m,&s);
        int u,v,w;
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&u,&v,&w);//输入起点终点权值
            addedge(u,v,w);//加边
        }
        spfa();//核心
        for(int i=1;i<=n;++i)
        {
            if(i==s) printf("0 ");
            else printf("%d ",dis[i]);
        }
        return 0;
    }

    end-

  • 相关阅读:
    0102-进程操作(面向对象简单工厂模式,打开输入文件)
    0101-进程操作(变量命名)
    win10无法成功完成操作因为文件包含病毒或潜在的垃圾软件如何处理
    序列化和反序列化
    __slot__的用法
    python中typeguard包
    pandas如何将多个DataFrame写入同一个excel工作簿中
    DEAP库遗传算法
    【教程】如何把喜马拉雅音频下载到电脑
    oracle安装路径查看方式
  • 原文地址:https://www.cnblogs.com/zhuier-xquan/p/10764441.html
Copyright © 2011-2022 走看看