<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      引言

      在圖論中,拓撲排序(Topological Sorting)是一種重要的算法,主要用于解決有向無環圖(DAG)中的依賴關系問題。它在任務調度、編譯器的依賴解析、課程安排等領域有著廣泛的應用。本文將詳細介紹拓撲排序的背景、定義、原理、應用場景,并通過偽代碼和具體代碼實現幫助讀者深入理解。最后,我們將結合一道題目,講解如何使用拓撲排序解決實際問題。


      背景知識

      有向無環圖(DAG)

      • 有向圖:由頂點和有向邊組成的圖,邊具有方向性。
      • 無環圖:圖中不存在任何有向環路。
      • 拓撲排序:只適用于有向無環圖(DAG),用于確定頂點的線性順序,使得對于每一條有向邊 $(u, v)$,頂點 $u$ 在頂點 $v$ 之前。

      拓撲排序的應用場景

      1. 任務調度:確定任務的執行順序,確保依賴任務先完成。
      2. 編譯器:解析源代碼中的依賴關系,確定編譯順序。
      3. 課程安排:確定課程的選修順序,確保先修課程在前。
      4. 項目管理:確定任務的優先級和順序。

      拓撲排序的定義

      給定一個有向無環圖 $G = (V, E)$,拓撲排序是將圖中所有頂點排成一個線性序列,使得對于每一條有向邊 $(u, v)$,頂點 $u$ 在頂點 $v$ 之前。

      示例

      假設有以下有向圖:

      1 -> 2 -> 3
      1 -> 4
      4 -> 3
      

      一個合法的拓撲排序是:1, 2, 4, 31, 4, 2, 3


      拓撲排序的原理

      拓撲排序的核心思想是通過不斷移除入度為 0 的頂點,并更新其鄰接頂點的入度,直到所有頂點都被移除或無法繼續移除。

      算法步驟

      1. 初始化
        • 計算每個頂點的入度(即指向該頂點的邊的數量)。
        • 將所有入度為 0 的頂點加入隊列。
      2. 處理隊列
        • 從隊列中取出一個頂點,將其加入拓撲排序結果。
        • 遍歷該頂點的所有鄰接頂點,將其入度減 1。如果某個鄰接頂點的入度變為 0,則將其加入隊列。
      3. 結束條件
        • 如果所有頂點都被處理,則拓撲排序成功。
        • 如果隊列為空但仍有未處理的頂點,則圖中存在環,拓撲排序失敗。

      拓撲排序的偽代碼

      function TopologicalSort(Graph):
          // 初始化
          in_degree = 計算每個頂點的入度
          queue = 所有入度為 0 的頂點
          result = 空列表
      
          // 處理隊列
          while queue 不為空:
              u = queue.pop()
              result.append(u)
              for each v in u 的鄰接頂點:
                  in_degree[v] -= 1
                  if in_degree[v] == 0:
                      queue.push(v)
      
          // 檢查是否所有頂點都被處理
          if result 的長度等于圖的頂點數:
              return result
          else:
              return "圖中存在環,無法進行拓撲排序"
      

      拓撲排序的代碼實現

      示例1

      ACWing 有向圖的拓撲序列
      以下是拓撲排序的 C++ 實現,結合題目要求輸出任意一個合法的拓撲序列,如果不存在則輸出 -1

      #include <iostream>
      #include <queue>
      #include <cstring>
      using namespace std;
      
      const int maxn = 1e5 + 7; // 定義最大頂點數和邊數
      int n, m; // 頂點數和邊數
      int in[maxn]; // 存儲每個頂點的入度
      int h[maxn], ne[maxn], t[maxn], idx; // 鄰接表存儲圖
      
      // 添加一條有向邊
      void add(int u, int v) {
          ne[++idx] = h[u];
          h[u] = idx;
          t[idx] = v;
      }
      
      // 拓撲排序函數
      void topo() {
          int cnt = 0; // 記錄已處理的頂點數
          queue<int> q, ans; // q 用于 BFS,ans 用于存儲拓撲排序結果
      
          // 將所有入度為 0 的頂點加入隊列
          for (int i = 1; i <= n; i++) {
              if (!in[i]) {
                  q.push(i);
                  cnt++;
                  ans.push(i);
              }
          }
      
          // BFS 遍歷
          while (!q.empty()) {
              int cur = q.front();
              q.pop();
      
              // 遍歷當前頂點的所有鄰接頂點
              for (int i = h[cur]; i != -1; i = ne[i]) {
                  int v = t[i];
                  in[v]--; // 鄰接頂點的入度減 1
                  if (!in[v]) { // 如果入度為 0,加入隊列
                      q.push(v);
                      cnt++;
                      ans.push(v);
                  }
              }
          }
      
          // 判斷是否所有頂點都被處理
          if (cnt != n) {
              cout << -1; // 存在環,無法進行拓撲排序
          } else {
              // 輸出拓撲排序結果
              while (!ans.empty()) {
                  cout << ans.front() << " ";
                  ans.pop();
              }
          }
      }
      
      int main() {
          ios::sync_with_stdio(false);
          cin.tie(0);
          cout.tie(0);
      
          cin >> n >> m; // 輸入頂點數和邊數
          memset(h, -1, sizeof(h)); // 初始化鄰接表
      
          // 輸入 m 條邊
          for (int i = 1; i <= m; i++) {
              int x, y;
              cin >> x >> y;
              in[y]++; // 更新入度
              add(x, y); // 添加有向邊
          }
      
          topo(); // 調用拓撲排序函數
      
          return 0;
      }
      

      代碼解析

      1. 數據結構

      • 鄰接表:使用數組 hnet 存儲圖。
      • 入度數組in[i] 表示頂點 $i$ 的入度。
      • 隊列:用于 BFS 遍歷和存儲拓撲排序結果。

      2. 拓撲排序函數

      • 初始化:將所有入度為 0 的頂點加入隊列。
      • BFS 遍歷:從隊列中取出頂點,更新其鄰接頂點的入度,并將入度為 0 的頂點加入隊列。
      • 結果輸出:如果所有頂點都被處理,則輸出拓撲排序結果;否則輸出 -1

      3. 主函數

      • 輸入處理:讀取頂點數、邊數以及邊的信息,并初始化鄰接表和入度數組。
      • 調用拓撲排序:執行拓撲排序并輸出結果。

      復雜度分析

      時間復雜度

      • 初始化:$O(n + m)$,其中 $n$ 是頂點數,$m$ 是邊數。
      • BFS 遍歷:$O(n + m)$。

      空間復雜度

      • 鄰接表:$O(m)$。
      • 隊列和入度數組:$O(n)$。

      示例2

      洛谷 雜物

      題目描述

      John 的農場有 ( n ) 個雜務需要完成,每個雜務需要一定的時間,并且某些雜務必須在其他雜務完成后才能開始。我們需要計算完成所有雜務所需的最短時間。

      輸入格式

      • 第 1 行:一個整數 ( n ),表示雜務的數量。
      • 第 2 到 ( n+1 ) 行:每行包含雜務的編號、完成時間以及其依賴的雜務列表(以 0 結束)。

      輸出格式

      • 一個整數,表示完成所有雜務所需的最短時間。

      樣例輸入 #1

      7
      1 5 0
      2 2 1 0
      3 3 2 0
      4 6 1 0
      5 1 2 4 0
      6 8 2 4 0
      7 4 3 5 6 0
      

      樣例輸出 #1

      23
      

      解題思路

      1. 問題分析

      • 每個雜務可以看作圖中的一個頂點。
      • 雜務之間的依賴關系可以看作有向邊。
      • 我們需要找到一種順序,使得所有雜務都能在其依賴的雜務完成后開始,并且總時間最短。

      2. 拓撲排序的應用

      • 拓撲排序可以用于解決有向無環圖(DAG)中的依賴關系問題。
      • 通過拓撲排序,我們可以確定雜務的執行順序,并計算完成所有雜務的最短時間。

      3. 算法設計

      1. 構建圖
        • 使用鄰接表存儲雜務之間的依賴關系。
        • 記錄每個雜務的入度(即依賴的雜務數量)。
      2. 拓撲排序
        • 使用優先隊列(堆)選擇當前可以執行的雜務(入度為 0)。
        • 更新雜務的完成時間,并將其依賴的雜務的入度減 1。
      3. 計算最短時間
        • 在拓撲排序過程中,記錄每個雜務的完成時間,并更新全局的最短時間。
        • 在本題中,我們設計利用一個優先隊列,保證當前任務耗時最長的依賴任務在最后出隊,這樣才能保證時間更新的正確性。

      我們結合示例進行分析:

      7
      1 5 0
      2 2 1 0
      3 3 2 0
      4 6 1 0
      5 1 2 4 0
      6 8 2 4 0
      7 4 3 5 6 0
      


      代碼實現

      以下是結合拓撲排序的 C++ 實現:

      #include <iostream>
      #include <queue>
      #include <cstring>
      using namespace std;
      
      const int maxn = 1e4 + 7; // 定義最大雜務數
      int n; // 雜務數量
      int in[maxn]; // 存儲每個雜務的入度
      int times[maxn]; // 存儲每個雜務的完成時間
      int h[maxn * 100], ne[maxn * 100], t[maxn * 100], idx; // 鄰接表存儲圖
      
      // 添加一條有向邊
      void add(int u, int v) {
          ne[++idx] = h[u];
          h[u] = idx;
          t[idx] = v;
      }
      
      // 拓撲排序函數
      void topo() {
          int mx = 0; // 記錄全局的最短時間
          priority_queue<pair<int, int>> q; // 優先隊列,存儲 (負完成時間, 雜務編號)
      	
          // 將所有入度為 0 的雜務加入隊列
          for (int i = 1; i <= n; i++) {
              if (!in[i]) {
                  q.push({-times[i], i});
              }
          }
      
          // BFS 遍歷
          while (!q.empty()) {
              auto cur = q.top();
              q.pop();
              int cur_time = -cur.first; // 當前雜務的完成時間
              int u = cur.second; // 當前雜務的編號
      		//最短完成時間但是取max的原因是需要等待耗時最長的依賴完成才能開始任務
              mx = max(mx, cur_time); // 更新全局的最短時間
      
              // 遍歷當前雜務的所有依賴雜務
              for (int i = h[u]; i != -1; i = ne[i]) {
                  int v = t[i];
                  in[v]--; // 依賴雜務的入度減 1
                  if (!in[v]) {
                      // 如果依賴雜務的入度為 0,將其加入隊列
                      q.push({-(cur_time + times[v]), v});
                  }
              }
          }
      
          // 輸出全局的最短時間
          cout << mx;
      }
      
      int main() {
          ios::sync_with_stdio(false);
          cin.tie(0);
          cout.tie(0);
      
          cin >> n; // 輸入雜務數量
          memset(h, -1, sizeof(h)); // 初始化鄰接表
      
          // 輸入每個雜務的信息
          for (int i = 1; i <= n; i++) {
              int id;
              cin >> id;
              cin >> times[id]; // 輸入雜務的完成時間
              int pre;
              cin >> pre;
              while (pre) {
                  in[id]++; // 更新雜務的入度
                  add(pre, id); // 添加依賴關系
                  cin >> pre;
              }
          }
      
          topo(); // 調用拓撲排序函數
      
          return 0;
      }
      

      代碼解析

      1. 數據結構

      • 鄰接表:使用數組 hnet 存儲雜務之間的依賴關系。
      • 入度數組in[i] 表示雜務 ( i ) 的入度(即依賴的雜務數量)。
      • 完成時間數組times[i] 表示雜務 ( i ) 的完成時間。
      • 優先隊列:用于選擇當前可以執行的雜務。

      2. 拓撲排序函數

      • 初始化:將所有入度為 0 的雜務加入優先隊列。
      • BFS 遍歷
        • 從隊列中取出雜務,更新其完成時間。
        • 遍歷其依賴的雜務,更新其入度。
        • 如果某個依賴雜務的入度為 0,則將其加入隊列。
      • 結果輸出:輸出全局的最短時間。

      3. 主函數

      • 輸入處理:讀取雜務數量、完成時間以及依賴關系,并初始化鄰接表和入度數組。
      • 調用拓撲排序:執行拓撲排序并輸出結果。

      總結

      拓撲排序是解決有向無環圖中依賴關系問題的有效算法。通過本文的介紹,讀者可以掌握拓撲排序的定義、原理、實現方法以及應用場景。結合代碼實現和題目講解,希望讀者能夠深入理解拓撲排序,并在實際問題中靈活運用。

      如果你對拓撲排序或其他圖論算法有更多疑問,歡迎在評論區留言討論!

      posted on 2025-02-11 09:07  Lostgreen  閱讀(618)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 精品国产自线午夜福利| 亚洲 日本 欧洲 欧美 视频| 久久这里只有精品首页| 无码人妻少妇色欲av一区二区| 欧美性猛交xxxx乱大交丰满| 亚洲精品一区| 亚洲男人第一无码av网站| 2020国产欧洲精品网站| 美女裸体黄网站18禁止免费下载| 欧美 亚洲 国产 制服 中文| 亚洲欧美中文日韩V日本| 无码人妻aⅴ一区二区三区蜜桃| 蜜芽久久人人超碰爱香蕉| 天美传媒mv免费观看完整| 丁香花成人电影| 乱老年女人伦免费视频| 拍摄av现场失控高潮数次| 97超级碰碰碰久久久久app| 伊人久久大香线蕉AV网禁呦| 平安县| 久久青草国产精品一区| 亚洲欧美人成网站在线观看看| 亚洲第一成人网站| 日韩中文字幕免费在线观看| 放荡的少妇2欧美版| 国产午夜精品福利91| 国产一区二区三区的视频| 国产精品午夜福利在线观看| 久久国产精品色av免费看| 国产成人高清亚洲一区二区| 成全我在线观看免费第二季| 久久久久久人妻一区精品| 东方四虎av在线观看| 亚洲色欲久久久久综合网| 国产精品一区二区日韩精品| 少妇无套内谢免费视频| 色www视频永久免费| 亚洲国产成人精品女人久久久| 婷婷四房播播| 无码专区 人妻系列 在线| 久久九九久精品国产免费直播|