• 传递闭包(Transitive Closure) POJ 3275


      有时候我们需要知道有向图 G = (V, E)的顶点之间是否存在路径。 那么怎样确定有向图中每对顶点之间是否存在路径呢? 这涉及到一个非常重要的概念—— 有向图的传递闭包。 

      有向图G的传递闭包定义为 G' = (V, E'), 其中 E' = {(u, v) | 图 G 中存在一条从顶点 u 到顶点 v 的路径}。

      我们可在O(n^3)(n为图G中的顶点数)的时间复杂度内计算出有向图的传递闭包。

      Floyd-Warshall算法(每对顶点间最短路径算法, 对其做了一些改动, 来计算传递闭包):

     1 // G = (V, E), V = {1, 2, ..., n}
     2 
     3 tu[i][j] = false for all possible i and j. // in fact 1 <= i, j <= n
     4 
     5 for each edge (u, v)
     6     tc[u][v] = true;
     7 
     8 for (k = 1; k <= n; k++)
     9      for (i = 1; i <= n; i++)
    10         for (j = 1; j <= n; j++) 
    11             tc[i][j] ||= (tc[i][k] && tc[k][j]);
    12 
    13 
    14 // then the transitive closure of G : G' = (V, E'), where E' = {(u, v) | tc[u][v] = true}. :)

       此外,存在时间复杂度为O(nm)的算法(n = |V|, m = |E|)。 算法的伪代码如下:

    // Give an O(VE)-time algorithm for computing the transitive closure of a directed
    // graph G = (V, E).
     
    // We compute the transitive closure as an adjacency matrix, TC[i, j]
     
    // Assume that each vertex v has a unique identification number: 
    // 
    //               id(v) ∈ {0, 1, 2, …, |V| - 1} 
    //
     
        for each vertex u 
            perform a BFS or DFS with u as the root 
            for each vertex v discovered in the search 
                TC[id(u), id(v)] = 1 
        return TC 
     
    // In the extreme case, a search can traverse every edge in the dataflow graph. Hence, 
    // the total time complexity of this algorithm is O(VE).

      POJ 3275:

           答案: n*(n-1)/2 - |E'|, E'为输入有向图的传递闭包的边集。

           C++代码:

     1 #include <stdio.h>
     2 
     3 #define N   1001
     4 #define M   10000
     5 
     6 int stack[N], top = -1;
     7 bool instack[N] = {false};
     8 
     9 struct s_edge {
    10     int node, next;
    11 } edge[M];
    12 int adj[N];
    13 
    14 int n, m;
    15 
    16 bool tc[N][N] = {false};
    17 
    18 void dfs(int u) {
    19     int v, i;
    20     for (i = adj[u]; i != -1; i = edge[i].next) {
    21         v = edge[i].node;
    22         if (!instack[v]) {
    23             stack[++top] = v;
    24             instack[v] = true;
    25             dfs(v);
    26         }
    27     }
    28 }
    29 
    30 int main() {
    31     int count, i, j, k;
    32     scanf("%d%d", &n, &m);
    33     for (i = 1; i <= n; i++) {
    34         adj[i] = -1;
    35     }
    36     for (i = 0; i < m; i++) {
    37         scanf("%d%d", &j, &k);
    38         edge[i].node = k;
    39         edge[i].next = adj[j];
    40         adj[j] = i;
    41     }
    42 
    43     // O(VE) or O(mn)
    44     for (count = 0, i = 1; i <= n; i++) {
    45          dfs(i);
    46          for (; top >= 0; top--) {
    47             j = stack[top];
    48             instack[j] = false;
    49             if (!tc[i][j]) {
    50                 tc[i][j] = true;
    51                 count++;
    52             }
    53          }
    54     }
    55     printf("%d
    ", n*(n-1)/2 - count);
    56     return 0;
    57 }
  • 相关阅读:
    [Swift]LeetCode1249. 移除无效的括号 | Minimum Remove to Make Valid Parentheses
    [Swift]LeetCode1240. 铺瓷砖 | Tiling a Rectangle with the Fewest Squares
    一位资深程序员大牛给予Java初学者的学习路线建议
    Java基础——集合源码解析 List List 接口
    Java定时任务调度详解
    Java实现CORS跨域请求
    假如时光倒流,我会这么学习Java
    Java后端程序员1年工作经验总结
    20个高级Java面试题汇总
    JVM内幕:Java虚拟机详解
  • 原文地址:https://www.cnblogs.com/william-cheung/p/3677469.html
Copyright © 2020-2023  润新知