刚刚AC的pj普及组第四题就是一种单源最短路。

我们知道当一个图存在负权边时像Dijkstra等算法便无法实现;

而Bellman-Ford算法的复杂度又过高O(V*E),SPFA算法便派上用场了。

其实SPFA 是用队列的优化,过程详见下图(PS:图片转自网络

好了,以上图片基本已经说明的SPFA的过程,下面就是代码实现:

模板如下:

void spfa(){
    ; i<=n; i++) dis[i]=INF; //初始化
    dis[start]=; inq[start]=;
    q.push(start);
    int i, v;
    while (!q.empty){
        v=q.front(); // 取队首节点
        q.pop();
        inq[v]=;   //释放节点,因为这节点可能下次被其他节点松弛,重新入队
        ; i<=n; i++)  //枚举所有顶点
            && dis[i]>dis[v]+a[v][i]){  //判断
                dis[i] = dis[v]+a[v][i];   //修改
                if (!inq[i]){ // 如果扩展结点i不在队列中,入队
                    q.push(i);
                    vis[i]=;
                }
           }
    }
}

可以看到,因为维护队列,和bfs有其曲同工之妙,但有一点不同!!!

bfs一旦入队,哪怕后面出队也无法在入队,而SPFA不同。

从数组名vis[i](BFS),inq[i](SPFA)可以看出定义不同。

那么对于有负权边,SPFA时间会大大增加……

不难想到DFS会不会快一点(好吧,既然都说了,肯定快,233)。

大约是O(E)。

代码如下:

void spfa(now){//DFS
    ; i<=edge[now]; i++)  //枚举从顶点now发出的边
       if (dis[to[now][i]>dis[now]+a[now][to[now][i]]){
        dis[to[now][i]=dis[now]+a[now][to[now][i]];
        spfa(to[now][i]);//继续DFS
       }
}

我们知道DFS其实是遍历到终点才换成另一条路,因此可以用来判断负权边!!

只需判断是否回到之前的节点即可,可以用 vis[i]  bool数组记录。

再看看Bellman-Ford算法,思路太简单,枚举点和边,就是时间比较长,为O(VE)。

代码如下:(转自百度百科)

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

#define MAX 0x3f3f3f3f
#define N 1010
int nodenum, edgenum, original; //点,边,起点  

typedef struct Edge //边
{
    int u, v;
    int cost;
}Edge;  

Edge edge[N];
int dis[N], pre[N];  

bool Bellman_Ford()
{
    ; i <= nodenum; ++i) //初始化
        dis[i] = (i == original ?  : MAX);
    ; i <= nodenum - ; ++i)
        ; j <= edgenum; ++j)
            if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost) //松弛(顺序一定不能反~)
            {
                dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
                pre[edge[j].v] = edge[j].u;
            }
            ; //判断是否含有负权回路
            ; i <= edgenum; ++i)
                if(dis[edge[i].v] > dis[edge[i].u] + edge[i].cost)
                {
                    flag = ;
                    break;
                }
                return flag;
}  

void print_path(int root) //打印最短路的路径(反向)
{
    while(root != pre[root]) //前驱
    {
        printf("%d-->", root);
        root = pre[root];
    }
    if(root == pre[root])
        printf("%d\n", root);
}  

int main()
{
    scanf("%d%d%d", &nodenum, &edgenum, &original);
    pre[original] = original;
    ; i <= edgenum; ++i)
    {
        scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost);
    }
    if(Bellman_Ford())
        ; i <= nodenum; ++i) //每个点最短路
        {
            printf("%d\n", dis[i]);
            printf("Path:");
            print_path(i);
        }
    else
        printf("have negative circle\n");
    ;
}  

看到核心部分,不难想到外层i跟内层循环无关,因此可以优化,即如果内层无松弛,可以提前结束!

这样一来,速度还是可以的……

之后我们看看dijkstra算法,其实就是贪心。

dis数组用来储存起始点到其他点的最短路。

转移方程为:

dis[i]=min(dis[i],dis[j]+w[j][i]|j为i能到达的点)

一开始dis[i]=INF,dis[start]=0;

很显然,不能处理有负边的情况……

时间为(V^2).两层循环解决。

注意每次选用没更新过的离源点最近的点对外拓展。

代码如下:

#include<stdio.h>
#include<stdlib.h>
#define INF 1<<28
#define N 1000+5
int a[N][N];
int d[N];
bool vis[N];
int i,j,k;
int m;//m代表边数
int n;//n代表点数
int main()
{
scanf("%d%d",&n,&m);
int mn;
int x,y,z;
;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[x][y]=a[y][x]=z;
}
;i<=n;i++)
d[i]=INF;
;i<=m;i++)
{
mn=INF;
;j<=n;j++)
if(!vis[j]&&d[j]<mn)
{
mn=d[j];
k=j;
}
vis[k]=;
;j<=n;j++)
&&d[j]>d[k]+a[k][j])
d[j]=d[k]+a[k][j];
}
;i<=n;i++)
printf("%d ",d[i]);
;
}

最后用最最最最……最智障的floyd算法结束今天学习(完全是为了凑齐四种算法,基本没啥可说)

直接看核心代码

; k<=n; k++)
 ; i<=n; i++)
 ; j<=n; j++)
 {
 if(w[i][j]>w[i][k]+w[k][j])
 w[i][j]=map1[i][k]+w[k][j];
 } 

注意最外层是循环中间的点!!!

其他就比较简单,不解释了,ok!

2018/1/28 每日一学 单源最短路的SPFA算法以及其他三大最短路算法比较总结的更多相关文章

  1. 2018/03/08 每日一学PHP 之 常量defind 和 const区别

    常量defind 和 const区别 什么是常量? 如字面理解的,在脚本执行期间不可改变的的量. 定义一个常量应该注意的事项? 1:常量默认大小写敏感,错误的大小写不会被识别为常量. 2:常量只能是标 ...

  2. 2018/1/27 每日一学 最长不降序子序列的O(n*logn)算法

    手动维护一个数组模拟即可,233-- 可以使用algorithm中的lower_bound(相当于二分) 代码如下: #include<cstdio> #include<algori ...

  3. 2018/05/02 每日一学Linux 之 .bash_profile和.bashrc的区别

    最近一直在学习其他,导致博客就疏忽了,很不好(其实就是自己懒了......). -- 为什么要使用 .bash_profile和.bashrc ? 在平常的使用中,有些文件夹或者命令很长,在执行时需要 ...

  4. 2018/04/18 每日一学Linux 之 ssh关闭密码登录

    在平常工作中,常常需要关闭 SSH 的密码登录,只留 SSH 证书登录. 好处显而易见,避免了经常输入密码导致的密码泄露,和设置密码导致被暴力破解的可能性. -- 方法也很简单,首先 你是可以 登录 ...

  5. 2018/03/28 每日一个Linux命令 之 mkdir/rmdir

    用于建立空文件夹和删除文件夹 -- 两命令重要参数 -p 递归建立/删除 -- 例如 mkdir -p demo1/demo2/demo3 建立demo3空文件夹,如果demo1/demo2没建立也建 ...

  6. 2018/03/10 每日一学PHP 之 修饰符 public/private/protected

    对于面向对象 修饰符的使用是我们最常用,也是很容易忽略的小细节. 对于编程来说,把握好每一个小细节,就能构造出漂亮,优雅的程序. public 使用最多的修饰符,公共方法,允许所有访问,就像一个公交车 ...

  7. 2018/03/09 每日一学PHP 之 require_once require include include_once 包含文件的区别

    require_once require include include_once 方法的区别 对于包含文件来说,如果只是使用框架来说的话,应该会很少碰到,因为框架底层对于文件的引用等做了很好的封装, ...

  8. 【luogu P3371 单源最短路径 】 模板 SPFA优化

    无优化:500ms deque优化:400ms #include <queue> #include <cstdio> #include <cstring> #inc ...

  9. 【luogu P3371 单源最短路径】 模板 SPFA

    题目链接:https://www.luogu.org/problemnew/show/P3371 我永远都喜欢Flyod.dijkstra + heap.SPFA #include <cstdi ...

随机推荐

  1. Robot Framework学习笔记(八)------ride标签使用

    一.edit标签使用 1.导入库 点击 Edit 标签页右侧的"Library"按钮,来添加库.在添加库之前,首先库已经在 Python 下进行了安装.如,添加"Sele ...

  2. Java常用命令与参数设置

    我介绍的JDK版本: 首先.介绍下JDK常用参数设置,如下是我个人环境的参数: -Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m 我们 ...

  3. react-native-scrollable-tab-view组件的简单使用

    react-native-scrollable-tab-view是一个滑动tab组件,可在tab之间进行切换显示内容 https://github.com/skv-headless/react-nat ...

  4. dt dl列表布局

    项目需要中 左边是房屋地址,右边显示几套房屋,如下图显示 ============ 开始想到使用 div标签,里面嵌套几个span,但是不利于对齐显示,一行还可以,多行了不方便 查找使用dl dt布局 ...

  5. BASIC-3 字母图形 循环 字符串

      基础练习 字母图形   时间限制:1.0s   内存限制:256.0MB        问题描述 利用字母可以组成一些美丽的图形,下面给出了一个例子: ABCDEFG BABCDEF CBABCD ...

  6. 强化学习之Q-learning ^_^

    许久没有更新重新拾起,献于小白 这次介绍的是强化学习 Q-learning,Q-learning也是离线学习的一种 关于Q-learning的算法详情看 传送门 下文中我们会用openai gym来做 ...

  7. 前端MVC Vue2学习总结(七)——ES6与Module模块化、Vue-cli脚手架搭建、开发、发布项目与综合示例

    使用vue-cli可以规范项目,提高开发效率,但是使用vue-cli时需要一些ECMAScript6的知识,特别是ES6中的模块管理内容,本章先介绍ES6中的基础与模块化的内容再使用vue-cli开发 ...

  8. easyHOOK socket send recv

    代码比较简单,就不做注释了.  包含一个sockethookinject.DLL 和sockethook.exe 有一点不清楚, SetExclusiveACL可以添加当前线程的hook, 但是eas ...

  9. 【zkw费用流】[网络流24题]餐巾计划问题

    题目描述 一个餐厅在相继的N天里,第i天需要Ri块餐巾(i=l,2,-,N).餐厅可以从三种途径获得餐巾. (1)购买新的餐巾,每块需p分: (2)把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f ...

  10. iOS中 学会如何对sqlite3 进行封装 (纯手工)

    #waring ---(看官注意) ---使用说明: ①在创建自定义model类之前让该类继承自文件中的Model类, ②为model类选一个NSString属性作为主键:(既,在初始化方法里面将从父 ...