【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点

题目链接:http://poj.org/problem?id=1236

题意:给定一个表示n所学校网络连通关系的有向图。现要通过网络分发软件,规则是:若顶点u,v存在通路,发给u,则v可以通过网络从u接收到。

现要求解两个问题:

TaskA: 最少分发给几个学校,就可以使所有的学校都能得到软件。

TaskB: 最少增加几条边,就可以使得,发软件给任一学校,所有学校都可以收到。

思路:先进行强联通分量分解,然后缩点,并计算缩点后每个点的出度、入度。入度为0的点的总数为 a ,出度为0的点总数为 b。a即TaskA的答案,而TaskB的答案为max(a, b)。

求SCC部分参考了 http://blog.csdn.net/dgq8211/article/details/7834292

缩点的做法很暴力,将每个强联通分量重新编号为一个集合,在求SCC时记录belong。

 1 #include <cstdio>
 2 #include <vector>
 3 #include <stack>
 4 #include <cstring>
 5 using namespace std;
 6 const int MAX_N = 105;
 7
 8 int n;
 9 vector<int> G[MAX_N];
10 stack<int> S;
11 int clock;
12 int scc;
13 int dfn[MAX_N], low[MAX_N];
14 int inStack[MAX_N];
15 int belong[MAX_N];
16 int indeg[MAX_N];//scc的
17 int outdeg[MAX_N];
18
19 void init(){
20     scc = 0;
21     clock = 0;
22     memset(dfn, 0, sizeof(dfn));
23     memset(low, 0, sizeof(low));
24     memset(inStack, 0, sizeof(inStack));
25     memset(indeg, 0, sizeof(indeg));
26     memset(outdeg, 0, sizeof(outdeg));
27 }
28
29 void tarjan(int u){
30     dfn[u] = low[u] = ++clock;
31     S.push(u);
32     inStack[u] = 1;
33     for(int i=0; i<G[u].size(); i++){
34         int v = G[u][i];
35         if(!dfn[v]){
36             tarjan(v);
37             low[u] = min(low[u], low[v]);
38         }else if(inStack[v]){
39             low[u] = min(low[u], dfn[v]);
40         }
41     }
42     if(dfn[u] == low[u]){
43         int v;
44         do{
45             v = S.top();
46             S.pop();
47             inStack[v] = 0;
48             belong[v] = scc;
49             //printf("%d ", v);
50         }while(v != u);
51         scc++;
52     }
53 }
54
55
56 int main()
57 {
58     freopen("1236.txt", "r", stdin);
59     scanf("%d", &n);
60     for(int i=1; i<=n; i++){
61         int u;
62         while(scanf("%d", &u) && u){
63             G[i].push_back(u);
64             //outdeg[i]++;
65             //indeg[u]++;
66         }
67     }
68     init();
69     for(int i=1; i<=n; i++){//遍历所有点
70         if(!dfn[i]){//未被发现的点
71             tarjan(i);
72         }
73     }
74     int a = 0;
75     int b = 0;
76
77     for(int i=1; i<=n; i++){//缩点
78         for(int j=0; j<G[i].size(); j++){
79             int u = G[i][j];
80             if(belong[i] != belong[u]){
81                 outdeg[belong[i]]++;
82                 indeg[belong[u]]++;
83             }
84         }
85     }
86
87     for(int i=0; i<scc; i++){
88         if(indeg[i] == 0) a++;
89         if(outdeg[i] == 0) b++;
90     }
91
92     b = max(a, b);
93     if(scc == 1) b = 0;
94     printf("%d\n%d\n", a, b);
95     return 0;
96 }

 

时间: 2024-10-27 11:30:36

【POJ 1236 Network of Schools】强联通分量问题 Tarjan算法,缩点的相关文章

[usaco]5.3.4强联通分支,双联通分支 Network of Schools

  Network of Schools IOI '96 Day 1 Problem 3 A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the "receiving schools

poj 2942 Knights of the Round Table 点重联通分量

  书上把这放在边联通的第一道题,于是一开始就按边写了,一直写不对,重新想了一遍,才发现是点联通-- /* author:jxy lang:C/C++ university:China,Xidian University **If you need to reprint,please indicate the source** */ #include <iostream> #include <cstdio> #include <cstdlib> #include <

spoj 208 Store-keeper bfs+重联通分量

     图论书上的一道练习题,却写了整整一周.     思路很简单,最简单的想法是bfs+dfs,就是没到拐点的时候dfs一次看是否可达,但是必然超时.所以就用重联通分量优化.判断拐点是否为割点,如果是割点,判断两边是否在一个重联通分量中.    一开始一直在考虑如何拐点是割点,两边的点也是不同重连通分量,但是这两个重联通分量并不是由拐点分割的怎么处理,想到了用个链表记录,然后二分判断,但感觉复杂度太高,于是迟迟没有动手敲.   后来才想明白因为两边的点必然与拐点有边连接,所以如果不在同一联通

【POJ 1330 Nearest Common Ancestors】LCA问题 Tarjan算法

题目链接:http://poj.org/problem?id=1330 题意:给定一个n个节点的有根树,以及树中的两个节点u,v,求u,v的最近公共祖先. 数据范围:n [2, 10000] 思路:从树根出发进行后序深度优先遍历,设置vis数组实时记录是否已被访问. 每遍历完一棵子树r,把它并入以r的父节点p为代表元的集合.这时判断p是不是所要求的u, v节点之一,如果r==u,且v已访问过,则lca(u, v)必为v所属集合的代表元.p==v的情况类似. 我的第一道LCA问题的Tarjan算法

poj 1861 Network MST

    期末后第一次写题,结果就是这么灵异的一题--     样例是错的,这题实际上就是求个最小生成树,spj /* author:jxy lang:C/C++ university:China,Xidian University **If you need to reprint,please indicate the source** */ #include <iostream> #include <cstdio> #include <cstdlib> #includ

poj 1144 Network 关节点

Tarjan练手题,WA了两次,处理回边误把low[u]=min(low[u],dph[v]); 写成low[u]=min(low[u],low[v]);了,还要注意节点数小于2时输出0 /* author:jxy lang:C/C++ university:China,Xidian University **If you need to reprint,please indicate the source** */ #include <iostream> #include <cstdi

UVa 10706 / POJ 1019 Number Sequence:打表及O(1)算法

10706 - Number Sequence Time limit: 3.000 seconds http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=19&page=show_problem&problem=1647 http://poj.org/problem?id=1019 Description A single positive integer i is giv

UVAoj 11324 - The Largest Clique(tarjan + dp)

题意:给定一个有向图,寻找一个点数最大集合,使得这个集合中的任意两个点u,v, 都有u->v 或者 v->u 或者u<==>v 思路:首先将强连通分量通过tarjan算法求出来,然后进行缩点,也就是每一个缩点所组成的图就是一个DAG图!令每一个点的权值就是这个缩点所包含节点(也就是对应的强连通分量的节点数目),因为强连通分量的任意的两个节点都是相互可达的,那么这个 缩点要么选要么不选,问题就转换成了DAG图上的最长路径! #include<iostream> #incl

poj 3352Road Construction(无向双连通分量的分解)

1 /* 2 题意:给定一个连通的无向图G,至少要添加几条边,才能使其变为强连通图(指的是边强联通). 3 思路:利用tarjan算法找出所有的双联通分量!然后根据low[]值的不同将双联通分量 4 进行缩点,最后图形会变成一棵树!也就是添加至少多少条边使一棵树变成强联通图! 5 6 知识点:若要使得任意一棵树,在增加若干条边后,变成一个双连通图,那么 7 至少增加的边数 =( 这棵树总度数为1的结点数 + 1 )/ 2 8 9 */ 10 #include<iostream> 11 #inc