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
作者:is_ok
出处:http://www.cnblogs.com/00isok/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。