Network of Schools
Time Limit:1000MS Memory Limit:10000KB 64bit IO Format:%I64d & %I64u
Description
Input
Output
Sample Input
5 2 4 3 0 4 5 0 0 0 1 0
Sample Output
1 2
强连通分量果然需要强行学习……看了一晚上大神博客才看懂。
强连通分量的具体内容:点击打开链接
本题掌握了博客内容之后感觉是个模板题。
A任务要求计算最少需多少个软件才能覆盖所有学校。毫无疑问只要计算入度为0的强连通分量的数量即可,入度不为0可依靠其他学校传输。
B任务要求计算添加几个学校到相应名单能够随便找一所学校就能覆盖所有学校,即添加几条边后可以使整个图变为强连通图。
一开始我的想法是对每一个强连通分量进行分析,若入度为0,出度也为0,则需要两条边;若入度和出度只有一个为0,则加一条边;若入度和出度都不为0,不用加边。
这个想法忘记考虑到边是相互的,加了边以后很明显是两个强连通分量都加了边。而加上去的边对一个强连通分量是入度+1,对另一个强连通分量就是出度+1。这对于后续判断需要增加的边数数量有很大困难。
网上找到的解答是取入度为0和出度为0的数量之中最大的作为需要增加的边的数量。我认为这种方法的优越性在于剥离了入度与出度的相互干扰,又保证了入度与出度的绝对不为0。
事实上假设入度为0为最大值,那么对于n个入度为0的强连通分量,有选择性所增加的n条边可将n个入度为0的不相连的强连通分量逐一连接。(此时若n个入度为0的强连通分量存在相互连接,那么可以用更少的边,比如m条,将他们直接相互连接,将n-m条用于连接n个入度为0的强连通分量以外的强连通分量。)
注意,由于出度为0的个数是小于等于入度为0的个数的。假设此时为相等的情况,只需n个强连通分量都和n个以外的某一个强连通分量连接即可。假设此时为出度为0个数小于入度为0个数的情况,那么在n条边连接的n个入度为0的强连通分量中,必定存在一个强连通分量出度为1,即与n个以外的强连通分量连接,此时根据图论,已经可以得知全图为强连通图。
因此这个解答是正确的,我的证明比较粗陋,但解答的结果却无误。
下面是代码:
#include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <cstdio> #include <stack> #include <cmath> #include <queue> #include <map> #include <set> using namespace std; #define N 105 vector<int> e[N]; int dfn[N], low[N],stap[N], stop,belong[N],outdeg[N],indeg[N],dindex, cnt; bool instack[N]; int n, m; void tarjan(int x){ int y = 0; dfn[x]=low[x]=++dindex; instack[x]=true; stap[++stop]=x; for (int i=0; i<e[x].size(); i++) { y=e[x][i]; if (!dfn[y]) { tarjan(y); if (low[y]<low[x]) { low[x]=low[y]; } } else if (instack[y]&&dfn[y]<low[x]){ low[x]=dfn[y]; } } if (dfn[x]==low[x]) { cnt++; while (y!=x) { y=stap[stop--]; instack[y]=false; belong[y]=cnt; } } } void solve(){ stop=dindex=cnt=0; memset(dfn, 0, sizeof(dfn)); memset(instack, 0, sizeof(instack)); memset(outdeg, 0, sizeof(outdeg)); memset(indeg, 0, sizeof(indeg)); for (int i=1; i<=n; i++) { if (!dfn[i]) { tarjan(i); } } } int main(){ int temp; while (~scanf("%d", &n)) { for (int i=1; i<=n; ++i) { e[i].clear(); } for (int i=1; i<=n; i++) { while (scanf("%d",&temp)) { if (temp==0) { break; } e[i].push_back(temp); } } solve(); for (int i=1; i<=n; i++) { int size=e[i].size(); for (int j=0; j<size; j++) { if (belong[i]!=belong[e[i][j]]) { outdeg[belong[i]]++; indeg[belong[e[i][j]]]++; } } } int sumout=0,sumin=0; for (int i=1; i<=cnt; i++) { if (outdeg[i]==0) { sumout++; } if (indeg[i]==0) { sumin++; } } if (cnt==1) { printf("1\n0\n"); } else printf("%d\n%d\n",sumin,max(sumin,sumout)); } return 0; }