POJ 1236 Network Of Schools (思维+强连通)

<题目链接>

题目大意:

有N个学校,每个学校之间单向可以发送软件,现在给你一些学校之间的收发关系。问你下面两个问题:至少要给多少个学校发送软件才能使得最终所有学校都收到软件;至少要多加多少个关系才能使得向任意一个学校发送一套软件,每个学校都能收到软件。 

解题分析:

首先,对该图进行缩点,显然第一问问的就是,缩点后的入度为0的联通块的数量(因为这些点没有入度,必须人为的给它们软件,它们才能接收到软件);第二问,显然就是问至少要加多少条边,使得该图变为强连通图,强连通图有个条件,就是所有的点一定要有出度和入度,所以我们可以让没有出度的点连上没有入度的点,等到出度或者入度为0的点不存在时,再把出度或入度为0的点补完(不能自己连自己,因为题目要求的是最少需要多少条边,所以考虑最优情况),注意,当整张图为连通图时,它的出度和入度均为0,max(1,1)=1,但是实际上不需要补边,所以这种情况需要特判。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <string>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7 
 8 const int M = 1e5 + 10;
 9 int n, m, u, v, tot, top, cnt, col;
10 struct node {
11     int v, next;
12 } edge[M];
13 int head[M], instack[M], stk[M];
14 int dfn[M], low[M], belong[M],in[M],out[M];
15 void init() {
16     tot = cnt = top = col = 0;
17     memset(stk, 0, sizeof(stk));
18     memset(head, -1, sizeof(head));
19     memset(dfn, 0, sizeof(dfn));
20     memset(instack, 0, sizeof(instack));
21 }
22 void add(int u, int v) {
23     edge[tot].v = v,edge[tot].next = head[u];
24     head[u] = tot++;
25 }
26 void tarjan(int u) {
27     dfn[u] = low[u] = ++col;    //col为遍历到该点的编号时间
28     instack[u] = 1;    //标记该元素是否在栈里
29     stk[top++] = u;
30     for (int i = head[u] ; ~i ; i = edge[i].next) {
31         int v = edge[i].u;        
32         if (!dfn[v]) {
33             tarjan(v);
34             low[u] = min(low[u], low[v]);
35         } 
36         else if (instack[v]) low[u] = min(low[u], dfn[v]);
37     }
38     if (dfn[u] == low[u]) {
39         cnt++;   //记录连通块的数量
40         int tmp;
41         do{
42             tmp = stk[--top];
43             instack[tmp] = 0;
44             belong[tmp] = cnt;     //给该连通块中的点染色
45         } while(tmp != u) ;
46     }
47 }
48 void solve() {
49     for (int i = 1 ; i <= n ; i++)
50         if (!dfn[i]) tarjan(i);
51 }
52 int main() {
53     while(scanf("%d", &n) != EOF) {
54         init();
55         memset(in, 0, sizeof(in));
56         memset(out, 0, sizeof(out));
57         for (int i = 1 ; i <= n ; i++) {
58             int v;
59             scanf("%d", &v);
60             while(v) {
61                 add(i, v);
62                 scanf("%d", &v);
63             }
64         }
65         for (int i = 1  ; i <= n ; i++)
66             if (!dfn[i]) tarjan(i);
67         for (int i = 1 ; i <= n ; i++) {
68             for (int j = head[i] ; ~j ; j = edge[j].next) {
69                 if (belong[edge[j].v] != belong[i]) {
70                     in[belong[edge[j].v]]++;    //统计每个连通块的出度和入度
71                     out[belong[i]]++;
72                 }
73             }
74         }
75         int sumin = 0, sumout = 0;
76         for (int i = 1 ; i <= cnt ; i++) {    //遍历每个连通块
77             if (!in[i]) sumin++;      //入度为0的连通分量个数
78             if (!out[i])sumout++;     //出度为0的连通分量个数
79         }
80         printf("%d\n", sumin);
81         if (cnt == 1) printf("0\n");   //如果该图已经是一个强连通图,只有一个强连通分量,则不需要加边
82         else printf("%d\n", max(sumin, sumout));       
83     }
84     return 0;
85 }

 

 

2018-09-30

posted @ 2018-09-30 23:58  悠悠呦~  阅读(175)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end