动态规划---解决多段图问题

ok  小伙伴们,我现在有点小小的红温,有点毛躁。

怎么解决多段图问题呢?求取最短路径有多种方法可取。

家人们,毫无思绪可言………………………………

要实现动态规划条件:子问题重叠度较高,并且满足当下(局部)最优解的要求。

1.Dijkstra算法(贪心算法)

单源最短路径的算法,即从图中某个指定的起点(源节点)到其他所有节点的最短路径。它适用于没有负权重边的图。算法的基本思想是逐步扩展最短路径,每次选择一个当前已知最短路径最小的未处理节点,并更新其邻居节点的最短路径。

  • 适用场景:该算法适用于边权非负的单源最短路径问题,即从一个源点到图中所有其他点的最短路径。
  • 主要特点:以起始点为中心向外层层扩展,直到扩展到终点为止。能得出最短路径的最优解,但由于遍历计算的节点较多,效率相对较低。
  • 时间复杂度朴素Dijkstra算法的时间复杂度为O(n^2+m),其中n表示点数,m表示边数。优化的Dijkstra算法适用于稀疏图,时间复杂度为O(mlogn)

理解重要点:

现在有点思路,Dijkstra 在我看来就是加点法。

现在需要两个集合,一个集合s[]存储目前在最短路径中的点,集合U[]存储未加入到最短路径中的点。

不断地找到原点v最短路径,加入相关点到s[i]集合中。

ok,现在我们要实现该算法

先设计图的存储方式,将“多段图中边权重”用动态数组比较好一点。

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define INF INT_MAX
#define Max_Vertex_Num 13

typedef struct Graph
{    int n;
    int e;
    int edges[Max_Vertex_Num][Max_Vertex_Num];
} Graph;

void CreateGraph(Graph *G)
{    int i, j, k, w;
    printf("请输入该图的边数和点数 :");
    scanf("%d%d", &G->e, &G->n);
    for (i = 0; i < G->n; i++)
        for (j = 0; j < G->n; j++)
            G->edges[i][j] = INF;
    printf("请输入%d 条边和对应权值:(a,b,c):\n", G->e);
    for (k = 0; k < G->e; k++)
    {    scanf("%d%d%d", &i, &j, &w);
        G->edges[i][j] = w; // 有向图
    }
}

void Dispath(Graph *G, int dist[], int path[], int s[], int v)
{    int i, j, k, d;
    int apath[Max_Vertex_Num];
    for (i = 0; i < G->n; i++)
    {    if (s[i] == 1 && i != v)
        {    printf("顶点%d到顶点%d路径长度为:%d\t 路径:", v, i, dist[i]);
            d = 0;
            apath[d] = i;
            k = path[i];
            if (k == -1)
            {    printf("不存在路径\n");
            }
            else
            {    while (k != v)
                {    d++;
                    apath[d] = k;
                    k = path[k];
                }
                d++;
                apath[d] = v;
                for (j = d; j >= 0; j--)
                    printf("%d ", apath[j]);
                printf("\n");
            }
        }
    }
}

void Dijsktra(Graph *G, int v)
{    int dist[Max_Vertex_Num], path[Max_Vertex_Num], s[Max_Vertex_Num];
    int mindis, i, j, u;

    for (i = 0; i < G->n; i++)
    {    dist[i] = G->edges[v][i];
        s[i] = 0;
        if (G->edges[v][i] < INF)
        {    path[i] = v;
        }
        else
        {    path[i] = -1;
        }
    }
    s[v] = 1;
    path[v] = -1; // 或者0,取决于定义,这里-1表示没有前驱节点

    for (i = 0; i < G->n - 1; i++)
    {    mindis = INF;
        for (j = 0; j < G->n; j++)
        {    if (s[j] == 0 && dist[j] < mindis)
            {    u = j;
                mindis = dist[j];
            }
        }
        s[u] = 1;
        for (j = 0; j < G->n; j++)
        {    if (s[j] == 0 && G->edges[u][j] < INF && dist[u] + G->edges[u][j] < dist[j])
            {    dist[j] = dist[u] + G->edges[u][j];
                path[j] = u;
            }
        }
    }

/*

  • G 是一个图的数据结构,其中 G->n 表示图中的节点数,G->edges 是一个二维数组,表示节点之间的边的权重。如果 G->edges[i][j] 是 INF(通常表示一个非常大的数,用来表示节点 i 和 j 之间没有直接的边),则意味着节点 i 和 j 之间没有直接的连接。
  • s 是一个布尔数组,用于跟踪哪些节点已经被处理过(即已经找到了从源节点到该节点的最短路径)。s[i] 为 0 表示节点 i 尚未处理,为 1 表示已处理。
  • dist 是一个数组,用于存储从源节点到每个节点的最短路径的估计距离。初始时,dist[source] 被设置为 0(假设源节点到自己的距离为0),而其他所有节点的 dist 值被设置为 INF(表示初始时,从源节点到其他节点的距离未知,或者说无穷大)。
  • path 是一个数组,用于记录最短路径上的前一个节点,以便最终可以重建最短路径。
  • INF 是一个非常大的数,用来表示无穷大或不存在的边。
  • 外层循环 (for (i = 0; i < G->n - 1; i++)):
    • 这个循环迭代 G->n - 1 次,因为一旦找到了从源节点到 n-1 个其他节点的最短路径,最后一个节点的最短路径也就自动确定了(假设图是连通的)。
  • 寻找当前最近的未处理节点 (mindis = INF; 和相关的内层循环):
    • 初始化 mindis 为 INF,然后遍历所有节点,寻找那些尚未处理(s[j] == 0)且当前估计距离(dist[j])最小的节点 u
    • 一旦找到这样的节点 u,就更新 mindis 为该节点的估计距离,并记住该节点 u
  • 标记节点 u 为已处理 (s[u] = 1;):
    • 将节点 u 标记为已处理,表示已经找到了从源节点到该节点的最短路径。
  • 更新其他节点的估计距离 (第二个内层循环):
    • 遍历所有节点 j,如果节点 j 尚未处理(s[j] == 0),并且存在一条从 u 到 j 的边(G->edges[u][j] < INF),且通过 u 到达 j 的路径比当前已知的通过其他路径到达 j 的路径更短(dist[u] + G->edges[u][j] < dist[j]),则更新 dist[j] 为新的更短的路径长度,并记录路径上的前一个节点为 upath[j] = u)。
  • */

    Dispath(G, dist, path, s, v);
}

int main()
{    int v = 1;
    Graph *G = (Graph *)malloc(sizeof(Graph));
    CreateGraph(G);
    int dist[Max_Vertex_Num], s[Max_Vertex_Num], path[Max_Vertex_Num];
    for (int i = 0; i < Max_Vertex_Num; i++)
    {    dist[i] = 0;
        s[i] = 0;
        path[i] = -1;
    }
    Dijsktra(G, v);
    free(G); // 释放内存
    return 0;
}

图例:

求从节点1到节点12的最短路径长度,以及路径中经过的节点数目。

Dijkstra代码:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#define INF INT_MAX
#define Max_Vertex_Num 13typedef struct Graph
{	int n;int e;int edges[Max_Vertex_Num][Max_Vertex_Num];
} Graph;void CreateGraph(Graph *G)
{	int i, j, k, w;printf("请输入该图的边数和点数 :");scanf("%d%d", &G->e, &G->n);for (i = 0; i < G->n; i++)for (j = 0; j < G->n; j++)G->edges[i][j] = INF;printf("请输入%d 条边和对应权值:(a,b,c):\n", G->e);for (k = 0; k < G->e; k++){	scanf("%d%d%d", &i, &j, &w);G->edges[i][j] = w; // 有向图}
}void Dispath(Graph *G, int dist[], int path[], int s[], int v)
{	int i, j, k, d;int apath[Max_Vertex_Num];for (i = 0; i < G->n; i++){	if (s[i] == 1 && i != v){	printf("顶点%d到顶点%d路径长度为:%d\t 路径:", v, i, dist[i]);d = 0;apath[d] = i;k = path[i];if (k == -1){	printf("不存在路径\n");}else{	while (k != v){	d++;apath[d] = k;k = path[k];}d++;apath[d] = v;for (j = d; j >= 0; j--)printf("%d ", apath[j]);printf("\n");}}}
}void Dijsktra(Graph *G, int v)
{	int dist[Max_Vertex_Num], path[Max_Vertex_Num], s[Max_Vertex_Num];int mindis, i, j, u;for (i = 0; i < G->n; i++){	dist[i] = G->edges[v][i];s[i] = 0;if (G->edges[v][i] < INF){	path[i] = v;}else{	path[i] = -1;}}s[v] = 1;path[v] = -1;for (i = 0; i < G->n - 1; i++){	mindis = INF;for (j = 0; j < G->n; j++){	if (s[j] == 0 && dist[j] < mindis){	u = j;mindis = dist[j];}}s[u] = 1;for (j = 0; j < G->n; j++){	if (s[j] == 0 && G->edges[u][j] < INF && dist[u] + G->edges[u][j] < dist[j]){	dist[j] = dist[u] + G->edges[u][j];path[j] = u;}}}Dispath(G, dist, path, s, v);
}int main()
{	int v = 1;Graph *G = (Graph *)malloc(sizeof(Graph));CreateGraph(G);int dist[Max_Vertex_Num], s[Max_Vertex_Num], path[Max_Vertex_Num];for (int i = 0; i < Max_Vertex_Num; i++){	dist[i] = 0;s[i] = 0;path[i] = -1;}Dijsktra(G, v);free(G); // 释放内存return 0;
}
运行结果显示:

最后得出了最短路径经过的点为1 3 6 10 12 ,最短路径距离 为16

注意!!!!上述输入了13个节点,是因为在图中有12个节点但是,节点是从0开始计算,因此直接写为13个节点,不会影响输出结果。

2.Bellman-Ford算法

  • 适用场景:适用于处理可能存在负权边的单源最短路径问题
  • 主要特点通过不断松弛来更新最短路径,可以判断图中是否存在负权回路(若存在,则不存在最短路)
  1. 处理负权边:可以处理图中包含负权边的情况。
  2. 检测负权环:能够检测图中是否存在负权环
  3. 时间复杂度:对于有V个顶点和E条边的图,算法的时间复杂度为O(VE)。

算法步骤:

  1. 初始化:将源点到自身的距离设为0,到所有其他顶点的距离设为无穷大(∞)。
  2. 松弛操作:对图中的所有边进行V-1次松弛操作。松弛操作是指如果通过某条边可以找到从源点到某个顶点的更短路径,则更新该路径的长度。
  3. 检测负权环:进行第V次遍历所有边,如果还能进行松弛操作,则说明图中存在负权环。
伪代码

function BellmanFord(Graph, source)
    // Step 1: Initialize distances from source to all vertices as INFINITE
    for each vertex v in Graph do
        distance[v] ← INFINITE
    distance[source] ← 0

    // Step 2: Relax all edges |V| - 1 times
    for i from 1 to |V| - 1 do
        for each edge (u, v) with weight w in edges do
            if distance[u] + w < distance[v] then
                distance[v] ← distance[u] + w

    // Step 3: Check for negative-weight cycles
    for each edge (u, v) with weight w in edges do
        if distance[u] + w < distance[v] then
            return "Graph contains a negative-weight cycle"

    return distance[]

Bellman-Ford代码:
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <stdbool.h>#define numV 5  // 假设图中有5个顶点// 用于表示图的结构
typedef struct Graph
{	int V;    // 顶点数int E;    // 边数int **adj; // 邻接矩阵
} Graph;// 创建图
Graph *createGraph(int V, int E)
{	Graph *graph = (Graph *)malloc(sizeof(Graph));graph->V = V;graph->E = E;graph->adj = (int **)malloc(V * sizeof(int *));for (int i = 0; i < V; ++i)graph->adj[i] = (int *)malloc(V * sizeof(int));return graph;
}// 添加边
void addEdge(Graph *graph, int src, int dest, int weight)
{	graph->adj[src][dest] = weight;
}// Bellman-Ford算法实现
void BellmanFord(Graph *graph, int src)
{	int V = graph->V;int dist[V];// 初始化距离数组for (int i = 0; i < V; ++i)dist[i] = INT_MAX;dist[src] = 0;// 松弛所有边 |V| - 1 次for (int i = 0; i < V - 1; ++i){	for (int u = 0; u < V; ++u){	for (int v = 0; v < V; ++v){	if (graph->adj[u][v] != 0 && dist[u] != INT_MAX && dist[u] + graph->adj[u][v] < dist[v]){	dist[v] = dist[u] + graph->adj[u][v];}}}}// 检测负权环for (int u = 0; u < numV; ++u){	for (int v = 0; v < V; ++v){	if (graph->adj[u][v] != 0 && dist[u] != INT_MAX && dist[u] + graph->adj[u][v] < dist[v]){	printf("Graph contains negative weight cycle\n");return;}}}// 打印最短路径for (int i = 0; i < numV; ++i)printf("Distance from %d to %d is %d\n", src, i, dist[i]);
}int main()
{	/* 创建如下图0/ \|   \1---2|    \3-----4*/int V = 5;  // 顶点数int E = 6;  // 边数Graph *graph = createGraph(V, E);addEdge(graph, 0, 1, -1);addEdge(graph, 0, 2, 4);addEdge(graph, 1, 2, 3);addEdge(graph, 1, 3, 2);addEdge(graph, 1, 4, 2);addEdge(graph, 3, 2, 5);addEdge(graph, 3, 1, 1);addEdge(graph, 4, 3, -3);BellmanFord(graph, 0);return 0;
}
运行结果:

  1. 3.SPFA算法(Shortest Path Faster Algorithm):

    • 适用场景:同样适用于处理存在负权边单源最短路径问题
    • 主要特点:是Bellman-Ford算法的队列优化版,通过维护一个队列来减少重复的操作次数,从而提高效率。
    • 时间复杂度:不稳定,最好情况下为O(E),最坏情况下退化为Bellman-Ford算法的时间复杂度O(VE)。其中E为边数,V为点数。

SPFA算法的基本思想:

  1. 初始化:将源点到自身的距离设为0,到所有其他顶点的距离设为无穷大。
  2. 使用队列:将源点加入队列。
  3. 循环处理:当队列非空时,循环执行以下操作:
    • 出队一个顶点u。
    • 遍历u的所有邻接点v,对每条边(u, v)进行松弛操作,如果通过u到达v的距离更短,则更新v的距离,并将v加入队列。
  4. 检测负权环:如果在执行松弛操作时,某个顶点已经被在队列中,并且再次被加入队列,则说明存在负权环。
SPFA算法的步骤:
  1. 初始化距离数组dist[],将源点到自身的距离设为0,其他顶点的距离设为无穷大。
  2. 将源点加入队列。
  3. 当队列非空时,执行以下操作:
    • 出队队列头部的顶点u。
    • 对于u的每一个邻接点v,如果通过u到达v的路径更短(即dist[u] + weight(u, v) < dist[v]),则更新dist[v],并将v加入队列。
  4. 如果在更新过程中,某个顶点被重复加入队列,则说明存在负权环。
相关代码:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>#define V 5  // 顶点数
#define E 7  // 边数int dist[V];  // 存储最短路径
int inQueue[V];  // 标记是否在队列中
int graph[V][V];  // 邻接矩阵表示图// 初始化图
void initGraph() {for (int i = 0; i < V; i++) {for (int j = 0; j < V; j++) {if (i == j) graph[i][j] = 0;else graph[i][j] = INT_MAX;}}
}// 添加边
void addEdge(int src, int dest, int weight) {graph[src][dest] = weight;
}// SPFA算法
bool SPFA(int src) {int queue[V], front = 0, rear = 0;for (int i = 0; i < V; i++) dist[i] = INT_MAX, inQueue[i] = 0;dist[src] = 0;queue[rear++] = src;inQueue[src] = 1;while (front != rear) {int u = queue[front++];inQueue[u] = 0;for (int v = 0; v < V; v++) {if (graph[u][v] != INT_MAX && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {dist[v] = dist[u] + graph[u][v];if (!inQueue[v]) {queue[rear++] = v;inQueue[v] = 1;}}}}for (int i = 0; i < V; i++) {if (dist[i] == INT_MAX) {printf("%d is not reachable from %d\n", i, src);} else {printf("Shortest path from %d to %d is %d\n", src, i, dist[i]);}}// 检测负权环for (int u = 0; u < V; u++) {for (int v = 0; v < V; v++) {if (graph[u][v] != INT_MAX && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) {return false;  // 存在负权环}}}return true;
}int main() {initGraph();addEdge(0, 1, -1);addEdge(0, 2, 4);addEdge(1, 2, 3);addEdge(1, 3, 2);addEdge(2, 1, 1);addEdge(2, 3, 1);addEdge(2, 4, 2);if (!SPFA(0)) {printf("Graph contains negative weight cycle\n");}return 0;
}

运行结果:

  1. 4.Floyd-Warshall算法:(动态规划)

    • 适用场景:适用于求解任意两点间的最短路径问题,可以正确处理有向图或负权的最短路径问题,适用于稠密图,即边的数量接近顶点数的平方的图。
    • 主要特点:通过动态规划的思想,逐步求出任意两点之间的最短路径。
    • 时间复杂度:对于有V个顶点的图,算法的时间复杂度为O(V^3)。
    • 空间复杂度:需要O(V^2)的空间来存储最短路径的中间结果。

算法步骤:

  1. 初始化:创建一个V×V的矩阵D,其中D[i][j]表示顶点i到顶点j的直接距离。如果i和j之间没有直接的边,则D[i][j]设为无穷大(∞)。对角线上的D[i][i]设为0。
  2. 迭代更新:对于每个顶点k,检查是否可以通过k来找到从i到j的更短路径。具体来说,对于每一对顶点i和j,如果D[i][j] > D[i][k] + D[k][j],则更新D[i][j] = D[i][k] + D[k][j]。
  3. 检测负权环:在算法结束后,检查对角线上的D[i][i]是否大于0,如果是,则图中存在负权环。

应用场景:

  • 适用于需要计算图中所有顶点对之间最短路径的问题。
  • 适用于不包含负权环的图。

限制:

  • 不适用于包含负权环的图。
  • 时间复杂度较高,对于大规模稀疏图可能效率较低。
伪代码

function FloydWarshall(Graph)
    // Step 1: Initialize the distance matrix
    for each node v from 1 to number_of_nodes do
        for each node w from 1 to number_of_nodes do
            if v == w then
                dist[v][w] ← 0
            else if there is an edge (v, w) then
                dist[v][w] ← weight(v, w)
            else
                dist[v][w] ← ∞

    // Step 2: Update the distance matrix
    for each node k from 1 to number_of_nodes do
        for each node i from 1 to number_of_nodes do
            for each node j from 1 to number_of_nodes do
                if dist[i][j] > dist[i][k] + dist[k][j] then
                    dist[i][j] ← dist[i][k] + dist[k][j]

    // Step 3: Check for negative-weight cycles
    for each node i from 1 to number_of_nodes do
        if dist[i][i] < 0 then
            return "Graph contains a negative-weight cycle"

    return dist

Floyd_Warshall算法相关代码:

Floyd-Warshall算法可以通过以下步骤实现:
  1. 初始化距离矩阵:创建一个二维数组dist[][],其中dist[i][j]表示顶点i到顶点j的直接距离。如果ij之间有直接的边,则dist[i][j]是边的权重;如果没有直接的边,则dist[i][j]设为无穷大(∞)。对角线上的dist[i][i]设为0。

  2. 迭代更新距离矩阵:使用三层循环,外层循环遍历每一个顶点k,内层两个循环遍历所有顶点ij。检查是否可以通过顶点k来找到从ij的更短路径。如果dist[i][k] + dist[k][j]小于当前的dist[i][j],则更新dist[i][j]

  3. 检测负权环:在算法结束后,再次遍历距离矩阵,如果发现任何对角线元素dist[i][i]小于0,则图中存在负权环。

#include <stdio.h>
#include <limits.h>#define V 4  // 定义顶点的数量// 打印解决方案矩阵
void printSolution(int dist[][V], int V) {for (int i = 0; i < V; i++) {for (int j = 0; j < V; j++) {if (dist[i][j] == INT_MAX)printf("%7s", "∞");elseprintf("%7d", dist[i][j]);}printf("\n");}
}// 实现Floyd-Warshall算法
void floydWarshall(int graph[][V]) {int dist[V][V], i, j, k;// 初始化距离矩阵for (i = 0; i < V; i++)for (j = 0; j < V; j++)dist[i][j] = graph[i][j];// 迭代更新距离矩阵for (k = 0; k < V; k++) {for (i = 0; i < V; i++) {for (j = 0; j < V; j++) {if (dist[i][k] != INT_MAX && dist[k][j] != INT_MAX && dist[i][k] + dist[k][j] < dist[i][j])dist[i][j] = dist[i][k] + dist[k][j];}}}// 打印解决方案矩阵printSolution(dist, V);
}// 主函数
int main() {int graph[V][V] = { { 0,   5,  INF, 10 },{ INF, 0,   3, INF },{ INF, INF, 0,   1 },{ INF, INF, INF, 0 } };floydWarshall(graph);return 0;
}
 运行结果:

      Vertex 0   Vertex 1   Vertex 2   Vertex 3
Vertex 0    0          5               8                9
Vertex 1    INF      0               3                4
Vertex 2    INF      INF            0                1
Vertex 3    INF      INF           INF              0

解释:

  • 从顶点0到顶点0的最短路径是0(自身到自身)。
  • 从顶点0到顶点1的最短路径是5。
  • 从顶点0到顶点2的最短路径是7(通过顶点1:0->1->2)。
  • 从顶点0到顶点3的最短路径是9(通过顶点1:0->1->3 或者 通过顶点2:0->2->3)。
  • 从顶点1到顶点2的最短路径是3。
  • 从顶点1到顶点3的最短路径是4(通过顶点2:1->2->3)。
  • 其他路径没有更短的路径,所以保持为INF

  1. 5.Johnson算法

    • 解决多源最短路径问题的有效算法,尤其适用于稀疏图。
    • 该算法通过重新赋权值,将带权图转化为一个满足三角形不等式的图,然后利用Dijkstra算法求解各顶点的最短路径。

Johnson算法的主要步骤如下:

  1. 新建虚拟节点:创建一个虚拟节点(通常编号为0),并从这个节点向所有其他节点连一条权重为0的边。

  2. 使用Bellman-Ford算法:使用Bellman-Ford算法(或SPFA算法)计算从虚拟节点到所有其他节点的最短路径,记为h_i。

  3. 重新赋予权重:对于图中每一条边(u, v),重新赋予权重w' = w + h(u) - h(v),其中w是原边的权重。

  4. 使用Dijkstra算法:以每个节点为起点,运行Dijkstra算法来计算到所有其他节点的最短路径。由于所有边的权重都已非负,可以使用Dijkstra算法。

正确性证明:

Johnson算法通过重新赋予权重的方式确保了所有边的权重非负,从而可以使用Dijkstra算法求解最短路径。这种方法的正确性在于,通过引入虚拟节点和重新赋予权重,算法实际上为每条路径增加了一个常数,这个常数与路径无关,因此不会影响路径之间的相对长度。

伪代码

function Johnson(graph):
    n = number of vertices in graph
    m = number of edges in graph
    D = array of size n * n initialized to infinity
    for each edge (u, v) in graph:
        D[u][v] = weight(u, v)
    
    // Step 1: Add a dummy source and calculate shortest paths to all vertices
    dummy = new vertex
    for each vertex v in graph:
        D[dummy][v] = 0
    for each vertex v in graph:
        BellmanFord(D, dummy, n)
    
    // Step 2: Adjust edge weights
    for each edge (u, v) in graph:
        D[u][v] += D[u][dummy] - D[v][dummy]
    
    // Step 3: Run Dijkstra's algorithm from each vertex
    for each vertex v in graph:
        sort edges by weight for Dijkstra's algorithm
        Dijkstra(D, v, n)
    
    return D

最终代码示例:

步骤1:添加虚拟节点并初始化距离数组

首先,我们添加一个虚拟节点(通常编号为0),并从这个节点向所有其他节点连一条权重为0的边。然后,我们使用Bellman-Ford算法计算从虚拟节点到所有其他节点的最短路径。

步骤2:重新赋予权重

使用每个顶点的势能值重新调整图中所有边的权重,确保所有边的权重都是非负的。

步骤3:使用Dijkstra算法计算所有顶点对的最短路径

在重新加权后的图上,对每个顶点应用Dijkstra算法来找到到所有其他顶点的最短路径。

            Vertex 0   Vertex 1   Vertex 2   Vertex 3
Vertex 0    0                2               -3               -2
Vertex 1    2                0                INF              4
Vertex 2    1                 INF             0                3
Vertex 3    INF              INF              INF           0

 代码示例:
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>#define numV 4
#define INF INT_MAXint dist[numV][numV];
int graph[numV][numV];// 用于Dijkstra算法的优先队列比较函数
int minDistance(int dist[][numV], bool sptSet[], int V)
{	int min = INF, min_index;for (int v = 0; v < V; v++)if (sptSet[v] == false && dist[v][0] < min)min = dist[v][0], min_index = v;return min_index;
}// 打印解决方案矩阵
void printSolution(int dist[][numV], int V)
{	for (int i = 0; i < V; i++){	for (int j = 0; j < V; j++){	if (dist[i][j] == INF)printf("%7s", "∞");elseprintf("%7d", dist[i][j]);}printf("\n");}
}// Dijkstra算法
void dijkstra(int graph[][numV], int V, int src, int dist[][numV])
{	bool sptSet[numV] = {false};for (int i = 0; i < V; i++)dist[src][i] = graph[src][i];dist[src][src] = 0;for (int count = 0; count < V - 1; count++){	int u = minDistance(dist, sptSet, V);sptSet[u] = true;for (int v = 0; v < V; v++)if (!sptSet[v] && graph[u][v] != INF && dist[src][u] != INF && dist[src][u] + graph[u][v] < dist[src][v])dist[src][v] = dist[src][u] + graph[u][v];}
}// Bellman-Ford算法
bool bellmanFord(int graph[][numV], int V, int src, int dist[][numV])
{	for (int i = 0; i < V; i++)for (int j = 0; j < V; j++)dist[i][j] = graph[i][j];for (int i = 0; i < V; i++)dist[i][i] = 0;for (int k = 0; k < V - 1; k++)for (int i = 0; i < V; i++)for (int j = 0; j < V; j++)if (dist[i][j] > dist[i][k] + dist[k][j])dist[i][j] = dist[i][k] + dist[k][j];for (int i = 0; i < V; i++)for (int j = 0; j < V; j++)if (dist[i][j] != graph[i][j] && graph[i][j] != 0)return false;return true;
}// Johnson算法
void johnson()
{	// Step 1: 使用Bellman-Ford算法计算势能值for (int i = 0; i < numV; i++)for (int j = 0; j < numV; j++)if (graph[i][j] == 0)graph[i][j] = INF;for (int i = 0; i < numV; i++)if (!bellmanFord(graph, numV, i, dist)){	printf("Graph contains a negative weight cycle\n");return;}// Step 2: 重新赋予权重for (int i = 0; i < numV; i++)for (int j = 0; j < numV; j++)if (graph[i][j] != INF)graph[i][j] = graph[i][j] + dist[i][0] - dist[j][0];// Step 3: 使用Dijkstra算法计算所有顶点对的最短路径for (int i = 0; i < numV; i++)dijkstra(graph, numV, i, dist);// 打印解决方案矩阵printSolution(dist, numV);
}int main()
{	// 初始化图int graph[numV][numV] ={	{0, -6, INF, -4},{-2, 0, -4, -2},{INF, INF, 0, 3},{INF, INF, INF, 0}};johnson();return 0;
}

运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于Spring Boot的在线性格测试系统设计与实现(源码+定制+开发)智能性格测试与用户个性分析平台、在线心理测评系统的开发、性格测试与个性数据管理系统

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

cMake编译github中源码

https://github.com/gflags/gflags 记录一下防止遗忘&#xff0c;本次编译为gflags库 1.下载CMake-gui https://cmake.org/ 安装时&#xff0c;选择自动创建环境变量&#xff0c;安装完输入&#xff0c;查看是否安装成功 cmake --version 2.下载源码&#xff0c;解压打开文…

web——upload-labs——第四关——.htaccess文件绕过

先尝试直接上传一个普通的一句话木马 显示此文件不允许上传&#xff0c;这道题并没有提示不允许上传什么后缀的文件&#xff0c;经过尝试&#xff0c;基本上所有后缀能够被解析为php语句执行的文件都不能成功上传。试试正常的图片能不能上传&#xff1a; 我们再来试试图片马能不…

Gitcode文件历史记录查看和还原

文件历史记录 文件历史记录用于记录代码文件的更改历史&#xff0c;它允许用户查看文件的不同版本&#xff0c;了解每个版本的修改内容、作者和提交消息。这对于跟踪文件演进、恢复错误更改、审查代码以及了解项目进展都非常有用。 文件历史记录功能提供了以下核心功能&#…

前端神经网络入门(二):如何利用 WebGPU 进行数值计算,实现大规模数据的计算加速?- 边读边按下F12跑代码吧

在 Web 应用开发中&#xff0c;JavaScript 是前端开发者们最常用的语言。然而&#xff0c;当面对大规模数据处理和计算任务时&#xff0c;JavaScript 在浏览器中的执行往往会受到诸多性能瓶颈的限制。幸运的是&#xff0c;WebGPU 的出现&#xff0c;为我们提供了在前端实现高性…

新手教学系列——善用 VSCode 工作区,让开发更高效

引言 作为一名开发者,你是否曾经在项目中频繁地切换不同文件夹,打开无数个 VSCode 窗口?特别是当你同时参与多个项目或者处理多个模块时,这种情况更是家常便饭。很快,你的任务栏上挤满了 VSCode 的小图标,切换起来手忙脚乱,工作效率直线下降。这时候,你可能会问:“有…

Oracle故障处理:ora-12514 与 ora-28547

目录 项目场景 问题分析 问题总结 项目场景 现场同事求助说&#xff1a;有个刚刚部署在Windows上的Oracle11gR2数据库&#xff08;单机单实例&#xff09;&#xff0c;使用PLSQL工具连接不上。帮忙查看后&#xff0c;发现未装Oracle client&#xff0c;指导其安装完Oracle客…

【miniMax开放平台-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被机器执行自动化程序攻击&#xff0c;存在如下风险&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露&#xff0c;不符合国家等级保护的要求。短信盗刷带来的拒绝服务风险 &#xff0c;造成用户无法登陆、注册&#xff0c;大量收到垃圾短信的…

26-ES集群搭建、身份认证配置

虚机搭建 添加es用户 elasticsearch 默认不允许root用户启动&#xff0c;所以需要创建es用户 useradd elasticsearch passwd elasticsearch 解压安装包 #解压es tar -xvzf elasticsearch-7.14.2-linux-x86_64.tar.gz 将文件夹赋予es用户权限 #将文件夹赋予es用户权限 sud…

OpenHarmony的公共事件

OpenHarmony的公共事件 公共事件简介 CES&#xff08;Common Event Service&#xff0c;公共事件服务&#xff09;为应用程序提供订阅、发布、退订公共事件的能力。 公共事件分类 公共事件从系统角度可分为&#xff1a;系统公共事件和自定义公共事件。 系统公共事件&#…

【AlphaFold3】开源本地的安装及使用

文章目录 安装安装DockerInstalling Docker on Host启用Rootless Docker 安装 GPU 支持安装 NVIDIA 驱动程序安装 NVIDIA 对 Docker 的支持 获取 AlphaFold 3 源代码获取基因数据库获取模型参数构建将运行 AlphaFold 3 的 Docker 容器 参考 AlphaFold3: https://github.com/goo…

npm list -g --depth=0(用来列出全局安装的所有 npm 软件包而不显示它们的依赖项)

您提供的命令 npm list -g --depth0 是在 Node Package Manager (npm) 的上下文中使用的&#xff0c;用来列出全局安装的所有 npm 软件包而不显示它们的依赖项。 这是它的运作方式&#xff1a; npm list -g --depth0-g: 指定列表应包括全局安装的软件包。--depth0: 限制树形结…

Spring Boot框架:电商系统的技术革新

4 系统设计 网上商城系统的设计方案比如功能框架的设计&#xff0c;比如数据库的设计的好坏也就决定了该系统在开发层面是否高效&#xff0c;以及在系统维护层面是否容易维护和升级&#xff0c;因为在系统实现阶段是需要考虑用户的所有需求&#xff0c;要是在设计阶段没有经过全…

【数据分享】中国对外投资合作发展报告(2013-2023)

数据介绍 绪 论............................................................................................................................. 1 对外投资合作高质量发展迈出新步伐................................................................... 2 第一篇 发…

【深度学习】LSTM、BiLSTM详解

文章目录 1. LSTM简介&#xff1a;2. LSTM结构图&#xff1a;3. 单层LSTM详解4. 双层LSTM详解5. BiLSTM6. Pytorch实现LSTM示例7. nn.LSTM参数详解 1. LSTM简介&#xff1a; LSTM是一种循环神经网络&#xff0c;它可以处理和预测时间序列中间隔和延迟相对较长的重要事件。LSTM通…

【云原生系列--Longhorn的部署】

Longhorn部署手册 1.部署longhorn longhorn架构图&#xff1a; 1.1部署环境要求 kubernetes版本要大于v1.21 每个节点都必须装open-iscsi &#xff0c;Longhorn依赖于 iscsiadm主机为 Kubernetes 提供持久卷。 apt-get install -y open-iscsiRWX 支持要求每个节点都安装 N…

【Hadoop】【hdfs】【大数据技术基础】实验三 HDFS 基础编程实验

实验三&#xff1a; HDFS Java API编程实践 实验题目 HDFS Java API编程实践 实验目的 熟悉HDFS操作常用的Java API。 实验平台 操作系统&#xff1a;Linux Hadoop版本&#xff1a;2.6.0或以上版本 JDK版本&#xff1a;1.6或以上版本 Java IDE&#xff1a;Eclipse 实验…

ssm114基于SSM框架的网上拍卖系统的设计与实现+vue(论文+源码)_kaic

摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff0c;商品拍卖当然也不能排除在外&#xff0c;随着商品拍卖管理的不断成熟&#xff0c;它彻底改变了过去传统的经营管理方式&#xff0c;不仅使商品…

Transformer中的算子:其中Q,K,V就是算子

目录 Transformer中的算子 其中Q,K,V就是算子 一、数学中的算子 二、计算机科学中的算子 三、深度学习中的算子 四、称呼的由来 Transformer中的算子 其中Q,K,V就是算子 “算子”这一称呼源于其在数学、计算机科学以及深度学习等多个领域中的广泛应用和特定功能。以下是…

厦大南洋理工最新开源,一种面向户外场景的特征-几何一致性无监督点云配准方法

导读 本文提出了INTEGER&#xff0c;一种面向户外点云数据的无监督配准方法&#xff0c;通过整合高层上下文和低层几何特征信息来生成更可靠的伪标签。该方法基于教师-学生框架&#xff0c;创新性地引入特征-几何一致性挖掘&#xff08;FGCM&#xff09;模块以提高伪标签的准确…