传递闭包(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*(n-1)/2 - count);
56     return 0;
57 }

 

转载于:https://www.cnblogs.com/william-cheung/p/3677469.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值