帶權有向圖中恰好k條邊的最短路徑


在帶權有向圖中,尋找恰好包含k條邊的最短路徑的問題,包括在遍歷恰好k條邊的情況下確定權重最小的路徑。這可以透過使用動態規劃技術來實現,例如使用三維陣列來儲存所有可能路徑的最小權重。該演算法迭代遍歷頂點和邊,在每一步更新最小權重。透過考慮所有恰好包含k條邊的可能路徑,該演算法確定圖中k條邊的最短路徑。

使用的方法

  • 樸素遞迴方法

  • 帶邊約束的Dijkstra演算法

樸素遞迴方法

樸素遞迴方法是一種基本且易於理解的解決問題的方法,它涉及將複雜問題分解成更小的子問題並遞迴地解決它們。在這種方法中,一個函式多次呼叫自身以解決子問題,直到達到基本情況。但是,由於重複計算和重疊子問題,它對於較大的問題例項可能效率低下。它需要諸如記憶化或動態規劃之類的最佳化技術。樸素遞迴方法易於理解和實現,但可能遭受指數時間複雜度。它通常用於小型問題或作為更最佳化演算法的起點。

演算法

  • 定義一個函式 `shortestPath(graph, u, v, k)`,它接收圖、源頂點u、目標頂點v和邊數k作為輸入。

  • 檢查基本情況

  • a. 如果k為0且u等於v,則返回0(因為在這種情況下不允許邊)。

  • b. 如果k為1且圖中u和v之間存在邊,則返回其權重。

  • c. 如果k小於或等於0,則返回無窮大(因為不允許負數或零條邊)。

  • 初始化一個變數res為無窮大,以儲存最短路徑距離。

  • 迭代遍歷圖中的所有頂點,如下所示

  • a. 如果從u到i存在邊(u和i不相等且i不等於v)

  • 遞迴呼叫shortestPath,其中i作為新的源頂點,v作為目標頂點,k-1作為剩餘的邊數。

  • 如果返回的結果不是無窮大,則將res更新為res和當前邊權重與遞迴結果之和的最小值。

  • 返回res的值作為恰好具有k條邊的最短路徑距離。

示例

#include <iostream>
#include <climits>

#define V 4
#define INF INT_MAX

int shortestPathWithKEdges(int graph[][V], int source, int destination, int k) {
    // Base cases
    if (k == 0 && source == destination)
        return 0;
    if (k == 1 && graph[source][destination] != INF)
        return graph[source][destination];
    if (k <= 0)
        return INF;

    // Initialize result
    int shortestPathDistance = INF;

    // Explore all adjacent vertices of the source vertex
    for (int i = 0; i < V; i++) {
        if (graph[source][i] != INF && source != i && destination != i) {
            int recursiveDistance = shortestPathWithKEdges(graph, i, destination, k - 1);
            if (recursiveDistance != INF)
                shortestPathDistance = std::min(shortestPathDistance, graph[source][i] + recursiveDistance);
        }
    }

    return shortestPathDistance;
}

int main() {
    int graph[V][V] = {
        {0, 10, 3, 2},
        {INF, 0, INF, 7},
        {INF, INF, 0, 6},
        {INF, INF, INF, 0}
    };
    int source = 0, destination = 3, k = 2;
    std::cout << "Weight of the shortest path is " << shortestPathWithKEdges(graph, source, destination, k) << std::endl;
    return 0;
}

輸出

Weight of the shortest path is 9

帶邊約束的Dijkstra演算法

帶邊約束的Dijkstra演算法是一種圖遍歷演算法,它查詢圖中源頂點到所有其他頂點之間的最短路徑。它考慮圖中邊的約束或限制,例如最大或最小邊權重。該演算法維護一個優先佇列來儲存頂點及其距離,並迭代地選擇具有最小距離的頂點。然後,它透過更新其相鄰頂點的距離(如果找到更短的路徑)來鬆弛相鄰頂點。這個過程持續到訪問所有頂點為止。帶邊約束的Dijkstra演算法確保所選路徑滿足所需的邊約束,同時找到最短路徑。

演算法

  • 建立一個帶有以下引數的Dijkstra函式:

  • 圖:帶有頂點和邊的輸入圖

    源:最短路徑的起始頂點

    約束:邊的約束或限制

    初始化一個空集合來儲存已訪問的頂點和一個優先佇列來儲存頂點及其距離。

  • 建立一個距離陣列,並將所有頂點的距離設定為無窮大,除了源頂點,其距離設定為0。

  • 將源頂點及其距離入隊到優先佇列。

  • 當優先佇列不為空時,執行以下操作:

  • 從優先佇列中出隊具有最小距離的頂點。

  • 如果該頂點尚未訪問,

  • 將其標記為已訪問。

  • 對於當前頂點的每個相鄰頂點

  • 應用邊約束以確定是否可以考慮該邊。

  • 計算從源頂點到相鄰頂點的新的距離,考慮邊權重和約束。

  • 如果新的距離小於當前距離,則更新距離陣列。

  • 將相鄰頂點及其新的距離入隊到優先佇列。

  • 訪問所有頂點後,距離陣列將包含從源頂點到滿足邊約束的每個頂點的最短距離。

  • 返回距離陣列作為結果。

示例

#include <iostream>
#include <vector>
#include <limits>

struct Edge {
    int destination;
    int weight;
};

void dijkstra(const std::vector<std::vector<Edge>>& graph, int source, std::vector<int>& distance) {
    int numVertices = graph.size();
    std::vector<bool> visited(numVertices, false);
    distance.resize(numVertices, std::numeric_limits<int>::max());
    distance[source] = 0;

    for (int i = 0; i < numVertices - 1; ++i) {
        int minDistance = std::numeric_limits<int>::max();
        int minVertex = -1;

        for (int v = 0; v < numVertices; ++v) {
            if (!visited[v] && distance[v] < minDistance) {
                minDistance = distance[v];
                minVertex = v;
            }
        }

        if (minVertex == -1)
            break;

        visited[minVertex] = true;

        for (const auto& edge : graph[minVertex]) {
            int destination = edge.destination;
            int weight = edge.weight;

            if (!visited[destination] && distance[minVertex] != std::numeric_limits<int>::max() &&
                distance[minVertex] + weight < distance[destination]) {
                distance[destination] = distance[minVertex] + weight;
            }
        }
    }
}

int main() {
    int numVertices = 4;
    int source = 0;
    std::vector<std::vector<Edge>> graph(numVertices);

    // Add edges to the graph (destination, weight)
    graph[0] = {{1, 10}, {2, 3}};
    graph[1] = {{2, 1}, {3, 7}};
    graph[2] = {{3, 6}};

    std::vector<int> distance;
    dijkstra(graph, source, distance);

    // Print the shortest distances from the source vertex
    std::cout << "Shortest distances from vertex " << source << ":\n";
    for (int i = 0; i < numVertices; ++i) {
        std::cout << "Vertex " << i << ": " << distance[i] << '\n';
    }

    return 0;
}

輸出

Shortest distances from vertex 0:
Vertex 0: 0
Vertex 1: 10
Vertex 2: 3
Vertex 3: 9

結論

本文概述了兩種用於解決帶權有向圖中關鍵問題的演算法。它解釋了樸素遞迴方法和帶邊約束的Dijkstra演算法。樸素遞迴方法涉及遞迴地探索所有恰好具有k條邊的可能路徑以找到最短路徑。帶邊約束的Dijkstra演算法使用優先佇列和距離更新來有效地找到從源頂點到圖中所有其他頂點的最短路徑。本文包含對演算法的詳細解釋,並提供示例程式碼來說明它們的用法。

更新於:2023年7月14日

227 次瀏覽

開啟你的職業生涯

完成課程獲得認證

開始學習
廣告