第三章 搜索与图论(二)——最短路问题


源点表示起点,汇点表示终点
image.png
一些认识:
m和 n 2 n^2 n2一个级别是稠密图,m和n一个级别是稀疏图
最短路问题不区分有向图与无向图,因为无向图是一种特殊的有向图,能解决有向图的最短路问题,就能解决无向图的最短路问题

单源最短路

起点确定,终点是除起点外的其他点
默认n表示点数,m表示边数

  1. 所有边权为正数
  • 朴素Dijkstra O( n 2 n^2 n2),和边数无关,用于稠密图
  • 堆优化版Dijkstra O(mlogn) m = n 2 n^2 n2,用于稀疏图
  1. 边权存在负数
  • Bellman-Ford O(nm)
  • SPFA O(m),最坏O(nm),是Bellman-Ford的优化

朴素Dijkstra

基于贪心思想,每次选择距离源点最近的点

维护一个s集合,存储已经确定最短路径的点
一开始该集合中只有源点,不断地将点加入到s集合中,直到图中所有点都属于s集合,最短路求解结束

如何将点加入s集合?

  1. 遍历图中不属于s的点,选择其中与源点距离最小的点加入s
  2. 对于此时不属于s的点,用新加入s的点尝试更新其他点的最短距离

重复以上两个步骤,直到图中所有点都属于s
如何用代码实现?
dis数组存储每个点与源点的最短距离,初始化:源点到源点的距离为0
g数组为邻接矩阵,存储图中每条边的权值(若是稀疏图则使用邻接表
vt数组用来维护s集合,存储该点是否在s中的bool值

  1. 初始化dis[源点的编号] = 0, dis[其他点] = +∞
  2. 遍历dis数组不属于s的点,选择其中与源点距离最短的点加入s
  3. 根据新加入s的点,更新dis数组中其他不属于s的点,假设新加入点的编号为x,`dis[y] = min(dis[y], dis[x] + g[x][y])
  4. 重复2,3两个步骤,直到所有点属于s

第三步中,对于已经确定最短路径的点(属于s中的点) ,需要根据该点更新其他不属于s中的点。此时进行操作dis[y] = min(dis[y], dis[x] + g[x][y]),其实不需要特别要求该点不属于s,因为属于s的点已经确定了最短距离,不论是否进行min操作都不影响已经确定的最短距离。所以在min操作之前判断该点是否属于s的操作是无关紧要的

模板:

bool vt[N];
int dis[N], g[x][y];

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    
    for (int i = 0; i < n; ++ i )
    {
        int x = -1;
        for (int j = 1; j <= n; ++ j)
            if (!vt[j] && (x == -1 || dis[j] < dis[x])) 
                x = j;

        vt[x] = true; // 从不属于s的点中选择距离源点最近的点
        for (int y = 1; y <= n; ++ y) // 用新加入的点更新其他点
            dis[y] = min(dis[y], dis[x] + g[x][y]);
    }
}

外循环迭代n次,每次选择与源点距离最近的点放入集合中,集合中的点为已经确定最短距离的点。迭代n次后,图中所有点都进入了s集合,即确定了所有点的最短距离
每一次迭代要做的事:

  1. 在不属于s中的点中,找到与源点距离最近的点
  2. 用该点更新其他(不属于s的)点

堆优化版Dijkstra

用堆优化朴素Dijkstra算法,时间复杂度可以达到O(mlogn)
若手写一个支持修改任意位置的堆,空间复杂度为O(n)
若使用优先队列,空间复杂度将达到O(m),存储稠密图比较浪费空间

朴素Dijkstra算法中,在不属于s的点中,找距离源点最近的点,时间复杂度为O(n)
外循环需要迭代n次,所以总得时间复杂度为O( n 2 n^2 n2),这是朴素Dijkstra算法的效率瓶颈
用堆结构对朴素算法进行优化,我们能以O(1) 的时间取出距离源点最近的点,不过代价就是提高了空间复杂度,从O(n) 提高到了O(m)。以及每次维护堆时,时间复杂度为O(logn)

同时,若使用邻接矩阵存储边,用新加入的点更新其他点时,时间复杂度为O(n),总的时间复杂度为O( n 2 n^2 n2)。也是一个瓶颈,不过很好解决,用邻接表存储边,总的时间复杂度为O(m)

而使用邻接表存储,不用考虑重边问题。邻接矩阵只能存储两点间的一条边,所以要选择重边中最小的边

代码如何实现?
用优先队列存储pairfirst为点到源点的距离,second为点的编号
由于优先队列无法修改元素,所以图中的每条边都会进入一次优先队列,此时存在冗余且无效的数据
具体情况是:队列中存在second相同的pair,此时first较大的pair为无效数据。但我们无法删除任意位置的pair,只能删除堆顶的pair。所以取出堆顶元素时需要判断该pairsecond是否已经在s中(最短距离是否已经确定
若已经在s中,说明不需要进行接下来的操作(加入s和用该点更新其他点),所以此时直接continue

模板:

typedef pair<int, int> PII;

int h[N], e[N], ne[N], w[N], idx = 1;
priority_queue<PII, vector<PII>, greater<PII>> q;
int dis[N];
bool vt[N];

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    q.push({ 0, 1 }), dis[1] = 0;
    
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        
        int x = t.second, d = t.first;
        if (vt[x]) continue;
        vt[x] = true;
        
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (!vt[y] && d + w[i] < dis[y])
            {
                dis[y] = d + w[i];
                q.push({ dis[y], y });
            }
        }
    }
}

Bellman Ford算法

暴力美学

用于处理负权边
存储边的结构没有要求,可以用简单的结构体存储

struct Edge
{
	int x, y, w;
}edges[M];

循环n次,每次都遍历所有边
遍历某条边时,对其进行松弛操作:dis[y] = min(dis[y], dis[x] + w[i]),其中x是当前边的起点,y是当前边的终点,w[i]是当前边的权重
每次的松弛操作其实是在尝试更新dis[y],而更新依赖于dis[x]。若dis[x]不是无穷大,而是被其他松弛操作更新了,那么dis[y]也可以被当前松弛操作更新

外循环与dis数组的关系:若循环进行了k次,dis数组存储从源点开始走,不超过k条边,递达其他点的最短距离

若存在负环,则最短路不一定存在(负环与源点和目标点不连通时则最短路存在
用Bellman-Ford判断负环:
第n次外循环时,若更新了dis数组,说明某条最短路中有n条边,即n+1个点,根据抽屉原理,该图存在负环
但Bellman-Ford的时间复杂度较高,一般用SPFA解决负环问题

三角不等式:dis[y] <= dis[x] + w

串联问题:每次对所有边进行松弛操作时,应该基于上一次对所有边进行松弛操作后的状态。什么意思呢?对所有边进行了一次松弛操作后,我们要备份此时的dis数组,作为上一次的状态保存
若不备份dis数组,而直接进行松弛操作。修改dis数组的某些数据后,此时的dis数组不再是上一次对所有边进行松弛操作后的状态,基于这个状态进行松弛操作得到的结果多半是错误的

串联问题与dp状态压缩导致的问题类似

模板:

struct Edge
{
	int x, y, w;
}edges[M];

int dis[N], bup[N];
void bellman_ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; ++ i )
    {
        memcpy(bup, dis, sizeof(dis));
        for (int j = 0; j < m; ++ j )
        {
            auto t = edges[j];
            dis[t.y] = min(dis[t.y], bup[t.x] + t.w);
        }
    }
}

SPFA

SPFA是对Bellman-Ford算法的一个优化
注意松弛操作:dis[y] = min(dis[y], dis[x] + w[i]) 什么时候这个操作才是有效的?只有当dis[x]变小,dis[y]才可能变小 当dis[x]不变时,dis[y]`一定不会变换,所以Bellman-Ford中,大部分的更新多余且无效

如何使得每次的更新都是有效的呢?
运用广搜的思想,将每次更新(有效松弛)的点进入队列
初始时,将源点入队,表示源点的最短路被更新
每次取出队头的点,对连接该点的所有边进行松弛操作。通过邻接表将与该点邻接的点入队,因为这些点的dis距离被更新了
直到队列为空,此时整张图更新完成

注意,为防止一个点的重复地进入队列,要维护每个点的状态:某个点入队之前,先判断该点是否已经在队列中

模板:

// N为点数,M为边数
int dis[N];
int h[N], e[M], ne[M], w[M], idx = 1;
int q[N], hh, tt = -1;
bool st[N];

void add(int x, int y, int d)
{
	e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void spfa()
{
	memset(dis, 0x3f, sizeof(dis));
	q[++ tt] = 1, st[1] = true, dis[1] = 0; // 假设1号点为源点
	while (tt >= hh)
	{
		int x = q[hh ++ ];
		st[x] = false;
		for (int i = h[x]; i != -1; i = ne[i] )
		{
			int y = e[i];
			if (dis[x] + w[i] < dis[y]) 
			{
				dis[y] = dis[x] + w[i];
				if (!st[y]) st[y] = true, q[++ tt ] = y;
			}
		}
	}
}

出题者可能故意卡SPFA的时间,使时间复杂度达到最坏的情况O(nm)
此时只能使用其他算法

SPFA求负环

和Bellman-Ford一样,SPFA也是运用抽屉原理判断图中是否有负环

在SPFA求负环的算法中,虽然求解过程与原SPFA差不多,但是有些思想是完全不一样的
比如dis数组不再表示其他点到源点的距离,可以认为该数组没有任何含义
初始化时,将dis数组的每个值置为0,甚至任意数都是可以的,只要保证每个位置的值一样
由于现在要判断图中是否存在负环,若从一个点出发找负环,在非连通图中可能无法实现,所以不能用单源点判断负环。所以初始化时,将所有的点入队,表示每个点都是源点,此时就算是非连通图也能找到负环
dis[x] + w[i] < dis[y]:什么时候会执行该操作?当dis所有值都相同时,只有w[i]为负数(存在负权边)时,该操作才会执行。此时维护cnt数组,cnt[y] = cnt[x] + 1表示遍历了某条存在负权边的路径中的一条边,当cnt数组中的某个值大于等于n,即cnt[i] >= n时,根据抽屉原理,该图中存在负环

所以,求解负环时,SPFA不再是单源求解过程,而是多源求解过程。求解的问题也不再是最短路,而是负环的判断
此时,dis数组的含义发生变化,同时引入cnt数组,存储遍历某条存在负权边的路径的边的次数

非连通图中的负环,边数不可能大于等于n吧。那么根据cnt[i] >= n判断图中是否存在负环有问题吗?cnt数组存储在某条存在负权边的路径中遍历的次数,当一个负环的边数小于n,那么我们将一直遍历这个负环,直到cnt[i] >= n停止
最坏的情况时,所有的点共同构成了负环,负环中点的数量与边的数量都等于n,此时我们将遍历图中所有点一次,接着cnt[i] == n,遍历结束,说明图中存在负环

模板:

int h[N], e[M], ne[M], w[M], idx = 1;
int q[N], hh, tt = -1;
int dis[N], cnt[n];
bool st[N];

void add(int x, int y, int d)
{
	e[idx] = y, ne[idx] == h[x], w[idx] = d, h[x] = idx ++ ;
}

bool spfa()
{
	for (int i = 1; i <= n; ++ i )
		q[++ tt] = i, st[i] = true;

	while (tt >= hh)
	{
		int x = q[hh ++];
		st[x] = false;
		for (int i = h[x]; i != -1; i = ne[i])
		{
			int y = e[i];
			if (dis[x] + w[i] < dis[y])
			{
				dis[y] = dis[x] + w[i];
				cnt[y] = cnt[x] + 1;
				if (cnt[y] >= n) return true;
				if (!st[y]) st[y] = true, q[++ tt] = y;
			}
		}
	}
	return false;
}

多源汇最短路

Floyd

任意起点与终点的最短路问题
Floyd的时间复杂度为: O( n 3 n^3 n3)

d[k][i][j]:从i号点出发,只经过1~k号这些中间点,到达j号点的最短距离
状态更新:d[k][i][j] = d[k - 1][i][k] + d[k - 1][k][j],观察表达式,发现第一个维度可以压缩,即d[i][j] = d[i][k] + d[k][j]

过程很简单,三重循环进行动态规划
模板:

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数组要初始化:初始化的值与邻接矩阵一样,由于要求解的问题是多源汇最短路,所以要初始化源点到源点的距离为0


最短路练习题

849. Dijkstra求最短路 I

849. Dijkstra求最短路 I - AcWing题库
对图中节点进行编号,题目要求1号点到n号点的最短距离
以1号点作为源点,用朴素Dijkstra更新dis数组,获取单源最短路

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

const int N = 510, M = 1e5 + 10;
int dis[N], g[N][N];
bool vt[N];
int n, m;

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    
    for (int i = 0; i < n; ++ i )
    {
        int x = -1;
        for (int j = 1; j <= n; ++ j)
            if (!vt[j] && (x == -1 || dis[j] < dis[x])) 
                x = j;

        vt[x] = true; // 从不属于s的点中选择距离源点最近的点
        for (int y = 1; y <= n; ++ y) // 用新加入的点更新其他点
            dis[y] = min(dis[y], dis[x] + g[x][y]);
    }
}

int main()
{
    memset(g, 0x3f, sizeof(g));
    
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        g[x][y] = min(g[x][y], d);
    }
    
    dijkstra();
    
    if (dis[n] == 0x3f3f3f3f) printf("-1\n");
    else printf("%d\n", dis[n]);
    
    return 0;
}

850. Dijkstra求最短路 II

850. Dijkstra求最短路 II - AcWing题库
image.png

题目给定的图为稀疏图,使用邻接表存储
和第一题一样,要求1号点到n号点的最短距离,以1号点为源点,用Dijkstra求得单源最短路

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

typedef pair<int, int> PII;
priority_queue<PII, vector<PII>, greater<PII>> q;
const int N = 2e5 + 10;
int h[N], e[N], ne[N], w[N], idx = 1;
int dis[N];
bool vt[N];
int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void dijkstra()
{
    memset(dis, 0x3f, sizeof(dis));
    q.push({ 0, 1 }), dis[1] = 0;
    
    while (q.size())
    {
        auto t = q.top();
        q.pop();
        
        int x = t.second, d = t.first;
        if (vt[x]) continue;
        vt[x] = true;
        
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (!vt[y] && d + w[i] < dis[y])
            {
                dis[y] = d + w[i];
                q.push({ dis[y], y });
            }
        }
    }
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    dijkstra();
    if (dis[n] == 0x3f3f3f3f) printf("-1\n");
    else printf("%d\n", dis[n]);
    
    return 0;
}

debug:优先队列需要建小堆,默认是建立大堆,这个真的没有注意到
queue<PII, vector<PII>, greater<PII>>:传greater建立小堆


853. 有边数限制的最短路

853. 有边数限制的最短路 - AcWing题库
image.png

求解具有负权边的最短路问题一般使用SPFA,因为其时间复杂度较低。但有些最短路问题只能用Bellman-Ford求解,因为这类题有边数的限制
限制的边数就是外循环的次数,内循环还是要对所有边进行松弛操作
由于图中可能存在负权边,当源点无法递达某一个点时,该点的dis距离可能不是最大值,而是小于最大值的较大值
假设源点无法到达目标点y点,但x点能到达y点,此时源点也无法递达x点,即dis[x] = +∞。而连接x与y的边权为负数,那么目标点y的dis距离将被更新:dis[y] = dis[x] + w,其中的dis[x]为正无穷,w为负数,那么dis[y]将被更新成一个小于正无穷的较大数
所以判断源点是否能递达其他点时,不能判断dis[i] == 0x3f3f3f3f,而应该判断dis[i] > 0x3f3f3f3f / 2

以下代码存在问题,无法AC

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

const int N = 510, M = 10010;
int dis[N], bup[N];
int n, m, k;

struct Edge
{
    int x, y ,w;
}edges[M];

int Bellman_Ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; ++ i )
    {
        memcpy(bup, dis, sizeof(dis));
        for (int j = 0; j < m; ++ j )
        {
            auto e = edges[j];
            dis[e.y] = min(dis[e.y], bup[e.x] + e.w);
        }
    }
    
    if (dis[n] > 0x3f3f3f3f / 2) return -1;
    return dis[n];
}

int main()
{
    scanf("%d%d%d", &n, &m, &k);
    int x, y, w;
    for (int i = 0; i < m; ++ i )
    {
        scanf("%d%d%d", &x, &y, &w);
        edges[i] = { x, y, w };
    }
    
    int t = Bellman_Ford();
    if (t == -1) puts("impossible");
    else printf("%d\n", t);
    
    return 0;
}

debug:某些最短路可能是负值,当最短路为-1时,Bellman_Ford()返回的t也是-1,此时-1不能作为区分是否存在最短路的值
所以应该直接在main函数中判断dis[n]0x3f3f3f3f的关系

以下是ac代码

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

const int N = 510, M = 10010;

struct Edge
{
    int x, y, w;
}edges[M];

int dis[N], bup[N];
int n, m, k;

void bellman_ford()
{
    memset(dis, 0x3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 0; i < k; ++ i )
    {
        memcpy(bup, dis, sizeof(dis));
        for (int j = 0; j < m; ++ j )
        {
            auto t = edges[j];
            dis[t.y] = min(dis[t.y], bup[t.x] + t.w);
        }
    }
}

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

851. spfa求最短路

851. spfa求最短路 - AcWing题库
image.png

debug:以下spfa是错误的,错在松弛操作的判断条件,当满足dis[x] + w[i] < dis[y]时,就要更新dis[y] = dis[x] + w[i]。然后再判断y是否在队列中,不能将y是否在队列作为松弛操作的判断条件

void spfa()
{
    memset(dis, 0x3f, sizeof(dis));
    q[++ tt] = 1, dis[1] = 0, st[1] = true;
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (!st[y] && dis[x] + w[i] < dis[y])
                st[y] = true, dis[y] = dis[x] + w[i], q[++ tt] = y;
        }
    }
}

以下是ac代码:

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

const int N = 1e5 + 10;

int h[N], e[N], ne[N], w[N], idx = 1;
int q[N], hh, tt = -1;
int dis[N];
bool st[N]; // 某个点是否在队列中
int n, m;


void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

void spfa()
{
	memset(dis, 0x3f, sizeof(dis));
	q[++ tt] = 1, st[1] = true, dis[1] = 0; // 假设1号点为源点
	while (tt >= hh)
	{
		int x = q[hh ++ ];
		st[x] = false;
		for (int i = h[x]; i != -1; i = ne[i] )
		{
			int y = e[i];
			if (dis[x] + w[i] < dis[y]) 
			{
				dis[y] = dis[x] + w[i];
				if (!st[y]) st[y] = true, q[++ tt ] = y;
			}
		}
	}
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    spfa();
    
    if (dis[n] == 0x3f3f3f3f) puts("impossible");
    else printf("%d\n", dis[n]);
    return 0;
}

852. spfa判断负环

852. spfa判断负环 - AcWing题库
image.png

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

const int N = 2010, M = 10010;
int h[N], e[M], ne[M], w[M], idx = 1;
int q[M], hh, tt = -1;
int dis[N], cnt[N];
bool st[N];
int n, m;

void add(int x, int y, int d)
{
    e[idx] = y, ne[idx] = h[x], w[idx] = d, h[x] = idx ++ ;
}

bool spfa()
{
    for (int i = 1; i <= n; ++ i )
        q[++ tt] = i, st[i] = true;
    
    while (tt >= hh)
    {
        int x = q[hh ++ ];
        st[x] = false;
        for (int i = h[x]; i != -1; i = ne[i])
        {
            int y = e[i];
            if (dis[x] + w[i] < dis[y])
            {
                dis[y] = dis[x] + w[i];
                cnt[y] = cnt[x] + 1;
                if (cnt[y] >= n) return true;
                if (!st[y]) q[++ tt] = y, st[y] = true;
            }
        }
    }
    return false; 
}

int main()
{
    memset(h, -1, sizeof(h));
    scanf("%d%d", &n, &m);
    int x, y, d;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &d);
        add(x, y, d);
    }
    
    if (spfa()) puts("Yes");
    else puts("No");
    
    return 0;
}

debug:用原生数组模拟队列时,队列的长度要设置为M,之前设置为int q[N],导致了TLE,排查了很久的逻辑错误,结果是这里错了。看来这种边界问题需要提前想好啊,懒得想就用STL的,但是时间差距嘛,看下图
image.png


854. Floyd求最短路

854. Floyd求最短路 - AcWing题库
image.png

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

const int N = 210, M = 20010;
int n, m, k;
int d[N][N];

void flody()
{
    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]);
}

int main()
{
    memset(d, 0x3f, sizeof(d));
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1; i <= n; ++ i ) d[i][i] = 0;
    
    int x, y, w;
    while (m -- )
    {
        scanf("%d%d%d", &x, &y, &w);
        d[x][y] = min(d[x][y], w);
    }
    
    flody();
    
    while (k -- )
    {
        scanf("%d%d", &x, &y);
        int t = d[x][y];
        if (t > 0x3f3f3f3f / 2) puts("impossible");
        else printf("%d\n", t);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用\[1\]提供了使用Python的networkx库绘制网络图和计算最短加权路径的示例代码。该代码使用了一个包含顶点和边的列表,并使用add_nodes_from和add_weighted_edges_from方法将它们添加到图中。然后,使用nx.shortest_path_length方法计算了从顶点v1到顶点v11的最短加权路径长度为13。\[1\] 引用\[2\]提供了一个计算最短路径的Python程序示例。该程序使用了numpy和networkx库。首先,定义了一个包含顶点和边的列表,并使用add_nodes_from和add_weighted_edges_from方法将它们添加到图中。然后,使用nx.shortest_path_length方法计算了最短路径长度,并将结果存储在一个字典中。接下来,使用numpy创建了一个6x6的零矩阵,并使用两个嵌套的for循环将最短路径长度填充到矩阵中。最后,使用矩阵乘法计算了运力,并找到了最小运力和对应的位置。\[2\] 引用\[3\]提供了关于Dijkstra算法的一些背景信息。Dijkstra算法是一种寻找最短路径的算法,适用于所有权重大于等于0的情况。它可以用于解决从一个起始点到任意一个点的最短路径问题。\[3\] 综上所述,如果你想在Python中计算图论中的最短路径,可以使用networkx库和Dijkstra算法。你可以根据引用\[1\]和引用\[2\]中的示例代码进行操作。 #### 引用[.reference_title] - *1* *3* [运筹学——图论与最短距离(Python实现)](https://blog.csdn.net/weixin_46039719/article/details/122521276)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [数学建模:图论模型 — 最短路模型示例 (Python 求解)](https://blog.csdn.net/qq_55851911/article/details/124776487)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值