图论-最短路

 一、不存在负权边-dijkstra算法

dijkstra算法适用于这样一类问题:

从起点 start 到所有其他节点的最短路径。

其实求解最短路径最暴力的方法就是使用bfs广搜一下,但是要一次求得所有点的最短距离我们不可能循环n次,这样复杂度太高,因此dijlstra算法应运而生,算法流程如下:

(待补充)

对于:

稠密图一般使用邻接矩阵+朴素dji

稀疏图使用邻接表+堆优化dji

1.1 朴素djikstra算法

算法模板:

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

const int N = 510;
int n,m;
int g[N][N];
int dist[N];
bool st[N];
int dijkstra(){
    memset(st,0,sizeof(st));
    memset(dist,0x3f,sizeof(dist));
    
    dist[1] = 0;
    //对于每个点都要遍历所有点,也即是穷举
    for(int i = 0;i < n; i++){
        int t = -1;//临时存储第i次处理的点
        //一、遍历所有点,在所有没有访问过的点中找到距离最近的点
        for(int j = 1; j<= n;j++)
            if(!st[j] && (t == -1 || dist[t] > dist[j])){
                t = j;
            }
            
        //
        st[t] = true;
        //二、用1-t的路径长度 + t - j的边长度来更新dist[j];
        for(int j = 1;j <= n;j++){
            dist[j] = min(dist[j],dist[t] + g[t][j]);
        }
    }
    if(dist[n] == 0x3f3f3f3f)return -1;
    return dist[n];

}

1.2 优先级队列优化的djikstra

首先我们分析,如果是稀疏图,什么导致的朴素版djikstra复杂度高,首先我们用的是邻接表来存图,那么就要用bfs相应的方法进行遍历,那么我们需要先while处理点再内层处理边,如果不优化,复杂度依旧是o(mn)

这是因为我们将所有的顶点都遍历了,用于寻找最小距离点,如果我们使用堆来优化(而不是一般bfs中的普通队列),每次使用优先级队列来存放顶点,因为理想情况下优先级队列中最多装 n 个节点,对优先级队列的操作次数和 m 成正比,所以整体的时间复杂度就是 O(mlogn)

红字解释:

因为在修改其它顶点最短距离的过程中,堆优化版本并没有遍历所有的顶点,而是遍历所有与当前选取的最小顶点有关的边 ,从一小部分顶点出发就能到达所有顶点,因此没有必要遍历所有顶点

优先级队列解释: 

priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;

pair中 第一个整数可以表示从源点到该顶点的距离,第二个整数表示顶点的编号。

vector<pair<int, int>>指定了底层容器类型。

greater<>:默认小顶堆,less<>:大顶堆排序

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

const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
//只存一个点,因为另一个点是邻接表的表头
struct Edge {
    int to, weight;
};

vector<Edge> adj[N];
int dist[N];
bool visited[N];

void dijkstra(int source) {
    memset(dist, 0x3f, sizeof(dist));
    memset(visited, 0, sizeof(visited));
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq;
    dist[source] = 0;
    pq.push({0, source});

    while (!pq.empty()) {
        int u = pq.top().second;
        pq.pop();

        if (visited[u]) continue;
        visited[u] = true;

        for (auto &e : adj[u]) {
            int v = e.to;
            int w = e.weight;
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                pq.push({dist[v], v});
            }
        }
    }
}

int main() {
    int n, m;
    cin >> n >> m;

    for (int i = 0; i < m; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        adj[x].push_back({y, z});
    }

    dijkstra(1); // 假设你想要从节点 1 找到到其他所有节点的最短路径

    if (dist[n] == INF) cout << "-1\n"; // 检查是否存在从1到n的路径
    else cout << dist[n] << "\n";

    return 0;
}

二、存在负权值 -bellma-ford算法

1.朴素Bellman-Ford算法

 三角不等式:

对于所有点都有:

dist[b] <= dist[a] + w

 松弛操作: 

dist[b] = min(dist[b], dist[a] + w)

 注意:如果一个图中存在负权回路,那么可能不存在最短路

#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;
const int N = 510;
const int M = 10010;
int n,m,k;
int dist[N],backup[N];
// 边,a表示出点,b表示入点,w表示边的权重
struct edge{
    int from;
    int to;
    int weight;
}edges[M];


void bellman_ford(){
    memset(dist,0x3f,sizeof dist);
    dist[1] =0;
// 如果第n次迭代仍然会松弛三角不等式(存在更新),就说明存在一条长度是n+1的最短路径,由抽屉原理,路径中至少存在两个相同的点,说明图中存在负权回路。
    for(int i = 0;i < k;i++){
// 备份,防止读后写
        memcpy(backup,dist,sizeof(dist));
        for(int j = 0;j < m; j++){
            int from = edges[j].from;
            int to  = edges[j].to;
            int weight = edges[j].weight;
            dist[to] = min(dist[to],backup[from] + weight);
        }
    }

}

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 0;i < m;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        edges[i] = {a,b,c};
    }
    bellman_ford();
    if(dist[n] > 0x3f3f3f3f/2){
        puts("impossible");
    }else{
        printf("%d\n",dist[n]);
    }
    
    return 0;
}

除了可以用邻接矩阵和邻接表外,还可用三元组存储图
允许存在负权边,而Dijkstra算法不允许
外循环次数决定最小路径的最大边数
若第n次迭代有修改,根据容斥原理知道,一定存在负权环(整个环的权重和为负数)
实际应用:换乘不超过k次的最短路径(限制路径的边数)
backup用于保存上次迭代的结果,避免“写后读”。Dijkstra算法不存在这种情况
由于存在负权回路(注意不是负权边),因此负权回路有可能把自定义的无穷大0x7f7f7f7f变小,由于最多修改10000×10000=108,10000×10000=108,而0x7f7f7f7f>2×108>2×108,故0x7f7f7f7f / 2依旧是“无穷大”,故可用dist[n] > 0x7f7f7f7f / 2判断是否是无穷大
时间复杂度为O(mn)

2.普通队列优化的bellman-ford算法

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

const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
//只存一个点,因为另一个点是邻接表的表头
struct Edge {
    int to, weight;
};

vector<Edge> adj[N];
int dist[N];
bool used[N];
int n, m,k;

void spfa(int source) {
    memset(dist, 0x3f, sizeof(dist));
    dist[source] = 0;
    queue<int> pq;
    pq.push(source);
    used[source] = true;
    while (!pq.empty()) {
        int u = pq.front();pq.pop();
        used[u] = false;
        for (auto &e : adj[u]) {
            int v = e.to;
            int w = e.weight;
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                if(!used[v]){
                    pq.push(v);
                    used[v] = true;
                }
                
            }
        }
    }
}

int main() {

    cin >> n >> m ;

    for (int i = 0; i < m; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        adj[x].push_back({y, z});
    }

    spfa(1);
    if(dist[n] > 0x3f3f3f3f/2){
        puts("impossible");
    }else{
        printf("%d\n",dist[n]);
    }

    return 0;
}

时间复杂度:

最好:o(m) 

最差:o(mn)

spfa相较于djikstra,如果题目中不进行针对性限制,一般是会比djikstra更快的

spfa求负环数量:

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

const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
//只存一个点,因为另一个点是邻接表的表头
struct Edge {
    int to, weight;
};

vector<Edge> adj[N];
int dist[N],cnt[N];
bool used[N];
int n, m,k;
bool iscir = false;

void spfa(int source) {
    // memset(dist, 0x3f, sizeof(dist));
    queue<int> pq;
    for(int i =1;i <= n;i++){
        // dist[i] = 0;
        pq.push(i);
        used[i] = true;
    }

    
    while (!pq.empty()) {
        int u = pq.front();pq.pop();
        used[u] = false;
        for (auto &e : adj[u]) {
            int v = e.to;
            int w = e.weight;
            if (dist[v] > dist[u] + w) {
                dist[v] = dist[u] + w;
                //判断是否存在负权值
                cnt[v] = cnt[u] + 1;
                if(cnt[v] >= n) {
                    iscir = true;
                    return;
                }
                if(!used[v]){
                    pq.push(v);
                    used[v] = true;
                }
                
            }
        }
    }
}

int main() {

    cin >> n >> m ;

    for (int i = 0; i < m; ++i) {
        int x, y, z;
        cin >> x >> y >> z;
        adj[x].push_back({y, z});
    }

    spfa(1);
    if(iscir){
        printf("Yes");
    }else{
        printf("No");
    }

    return 0;
}

三、Floyd算法

const int INF = 1E9;
// 初始化:
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// 算法结束后,d[a][b]表示a到b的最短距离
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

最短距离需要把d[i][i] = 0;
时间复杂度为O(n3) 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/496490.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue.js——学习计划表

1&#xff09;准备工作 ①打开D:\vue\chapter02\ learning_schedule 目录&#xff0c;找到 index.html 文件。 在文件中引 入BootStrap 样式文件&#xff0c;具体代码如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8&qu…

vivado 手动布线

手动路由 手动路由允许您为网络选择特定的路由资源。这给了你对信号将要采用的路由路径的完全控制。手动路由不调用route_design。路线在路线数据库中直接更新。当您想精确控制网络的延迟时&#xff0c;可能需要使用手动路由。对于例如&#xff0c;假设有一个源同步接口&#…

面试题--3.18

1. http与https的区别&#xff0c;以及https的认证过程及加密算法 &#xff1f; 区别&#xff1a; https协议需要到CA申请证书&#xff0c;一般免费证书较少&#xff0c;因而需要一定费用。 http是超文本传输协议&#xff0c;信息是明文传输&#xff0c;https则是具有安全性…

大型语言模型:技术回顾

公众号&#xff1a;Halo 咯咯&#xff0c;欢迎关注~ 简介 很难说自然语言处理&#xff08;NLP&#xff09;的旅程是什么时候开始的。根据维基百科的文章《自然语言处理的历史》[1]&#xff0c;它可能始于 17 世纪&#xff0c;当时莱布尼茨和笛卡尔试图理解不同语言中单词之间的…

让人担心的软件生态

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 其实很久之前shigen就想写这样的一篇文章&#xff0c;思考现在的软件生态和我们…

c语言数据结构(9)——插入排序、希尔排序

欢迎来到博主的专栏——C语言数据结构 博主ID&#xff1a;代码小豪 文章目录 排序插入排序希尔排序 排序 现在有N个数据的序列&#xff0c;其对应的序列号为[r1 ,r2 ……rn];将该序列对应的数据[k1 ,k2 ……kn]排成满足递减或递减的序列的操作称为排序 插入排序 玩过斗地主…

tomcat配置静态资源后无法正常访问

目录 一、场景二、配置三、访问异常四、排查五、原因六、解决 一、场景 1、将前端文件存在到指定目录 2、在tomcat配置静态资源 3、配置后无法正常访问到前端文件 二、配置 1、tomcat配置 2、静态资源 三、访问异常 四、排查 可以ping通&#xff0c;但是访问不了3080端口 …

探究WordPress受欢迎的原因及其org和com的区别

在当今互联网时代&#xff0c;WordPress已经成为了建立网站的首选工具之一&#xff0c;其受欢迎程度远远超出了其他竞争对手。那么&#xff0c;为什么WordPress如此受欢迎呢&#xff1f;让我们一起探究一下。 首先&#xff0c;WordPress是一个开源项目&#xff0c;这意味着任何…

【UEditorPlus】后端配置项没有正常加载,上传插件不能正常使用

解决办法&#xff1a; 1、找到UEditorPlus的根目录&#xff0c;修改 ueditor.all.js 文件 搜索&#xff1a;isJsonp utils.isCrossDomainUrl(configUrl); 更改为&#xff1a;isJsonp false; 2、重新运行前端即可正常使用 如果出现依旧不行&#xff0c;请关闭服务&#xff…

如何选择适合自己的办公空间

说到办公地点的选择&#xff0c;其实就跟挑衣服似的&#xff0c;得看场合、看需求&#xff0c;还得看个人喜好。有的人喜欢自由自在&#xff0c;有的人则需要稳定和私密。所以&#xff0c;咱们来看看哪些朋友更适合哪种办公环境。 适合共享办公室的&#xff1a; 刚起步的小公司…

教师的晋升通道:走向专业成长的阶梯

教师是一项需要不断学习、不断进步的职业。随着教育改革的不断深入&#xff0c;教师的晋升通道也越来越受到关注。本文将从教师的晋升通道、晋升标准和未来发展方向等方面进行探讨&#xff0c;旨在帮助广大教师了解自己的职业成长路径&#xff0c;促进个人发展。 一、教师的晋升…

rtph264depay插件分析笔记

1、rtp协议头 2、rtp可以基于TCP或者UDP 其中基于TCP需要加4个字节的RTP标志 3、rtph264depay定义解析函数gst_rtp_h264_depay_process&#xff0c;通过RFC 3984文档实现。 static void gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass) {GObjectClass *gobject…

RTSP应用:实现视频流的实时推送

在实现实时视频流推送的项目中&#xff0c;RTSP&#xff08;Real Time Streaming Protocol&#xff09;协议扮演着核心角色。本文将指导你通过安装FFmpeg软件&#xff0c;下载并编译live555&#xff0c;以及配置ffmpeg进行视频流推送&#xff0c;来实现一个基本的RTSP流媒体服务…

WIN使用LPD协议来共享打印机含统信UOS

打开“控制面板”&#xff0c;“程序和功能”&#xff0c;“启动或关闭Windows功能”&#xff0c;下拉找到“打印和文件服务”&#xff0c;勾选“LPD打印服务”和“LPR端口监视器”。确定之后重启电脑&#xff0c;共享主机和其它需要添加共享打印机的都开启功能和重启。 一、启…

SpringMVC设置全局异常处理器

文章目录 背景分析使用ControllerAdvice&#xff08;RestControllerAdvice&#xff09;ExceptionHandler实现全局异常全局异常处理-多个处理器匹配顺序存在一个类中存在不同的类中 对于过滤器和拦截器中的异常&#xff0c;有两种思路可以考虑 背景 在项目中我们有需求做一个全…

定时器的原理和应用

#include<reg51.h> unsigned char s[]{0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; unsigned char count0,num0; void inittimer() {TMOD0x01;//0000 0001TH0(65536-50000)/256; //定时50ms50000us 2562^8 初值向右边移动8位TL0(65536-50000)%256;ET01;//开启定…

TouchGFX之Button

TouchGFX中的按钮是一种感应触控事件的控件&#xff0c;能够在按钮被按下/释放时发送回调 代码 #ifndef TOUCHGFX_ABSTRACTBUTTON_HPP #define TOUCHGFX_ABSTRACTBUTTON_HPP #include <touchgfx/Callback.hpp> #include <touchgfx/events/ClickEvent.hpp> #includ…

面试题目--3.19

1.foo()和foo()之间有什么区别&#xff1f; 代表所有的warning忽略 2.什么是csrf攻击&#xff1f;如何防范&#xff1f; csrf&#xff0c;跨站请求伪造&#xff0c;攻击方伪装用户身份发送请求从而窃取信息或者破坏系统。 基本原理&#xff1a;用户访问a网站登录并生成了coo…

opencv 十九 python下实现多线程间rtsp直播流的复用

在多线程拉流的任务场景中&#xff0c;有时需要将一个rtsp拉取多次&#xff0c;每重新打开一次rtsp视频流就要多消耗一次带宽&#xff0c;为此基于类的静态对象实现rtsp视频流的复用。 1、实现代码 import threading import cv2,time #接收摄影机串流影像&#xff0c;采用多线…

论文《Exploring to Prompt for Vision-Language Models》阅读

论文《Exploring to Prompt for Vision-Language Models》阅读 论文概况论文动机&#xff08;Intro&#xff09;MethodologyPreliminaryCoOp[CLASS]位置Context 是否跨 class 共享表示和训练 ExperimentsOverall ComparisonDomain GeneralizationContext Length (M) 和 backbon…