(转载) poj1236 - Network of Schools
看到一篇挺好的代码,适合初学者,转载自 博主 wangjian8006
原地址:http://blog.csdn.net/wangjian8006/article/details/7888558
题目大意:有N个学校,从每个学校都能从一个单向网络到另外一个学校,两个问题
1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。
2:至少需要添加几条边,使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
解题思路:
首先找连通分量,然后看连通分量的入度为0点的总数,出度为0点的总数,那么问要向多少学校发放软件,就是入度为零的个数,这样才能保证所有点能够找到
然后第二问添加多少条边可以得到使整个图达到一个强连通分量,答案是入度为0的个数和出度为0的个数中最大的那个
为什么会这样呢,经过我同学的讨论,将这个图的所有子树找出来,然后将一棵子树的叶子结点(出度为0)连到另外一棵子树的根结点上(入度为0),这样将所有的叶子结点和根节点全部消掉之后,就可以得到一整个强连通分量,看最少多少条边,这样就是看叶子结点和根节点哪个多,即出度为0和入度为0哪个多。我只能说这种策略是正确的,适用于一般图,但其实我觉得也不能够很有力的证明这个结论成立
1 /* 2 kosaraju 3 Memory 224K 4 Time 0MS 5 */ 6 #include <iostream> 7 using namespace std; 8 #define MAXV 110 9 #define max(a,b) (a>b?a:b) 10 11 int map[MAXV][MAXV],order[MAXV],belong[MAXV],indegree[MAXV],outdegree[MAXV]; 12 int n,num,count; 13 bool vis[MAXV]; 14 15 void dfs(int x){ 16 int i; 17 vis[x]=1; 18 for(i=1;i<=n;i++) 19 if(map[x][i] && !vis[i]) 20 dfs(i); 21 order[++num]=x; 22 } 23 24 void dfst(int x){ 25 int i; 26 belong[x]=count; //记录结点属于哪个连通分量 27 vis[x]=1; 28 for(i=1;i<=n;i++) 29 if(!vis[i] && map[i][x]) 30 dfst(i); 31 } 32 33 void kosaraju(){ 34 int i; 35 memset(vis,0,sizeof(vis)); 36 num=count=0; 37 for(i=1;i<=n;i++) //第一次搜索将时间戳从小到大排序 38 if(!vis[i]) dfs(i); 39 memset(vis,0,sizeof(vis)); 40 41 for(i=n;i>=1;i--) //第二次搜索从时间戳大的开始走找连通分量 42 if(!vis[order[i]]){ 43 count++; //连通分量个数 44 dfst(order[i]); 45 } 46 } 47 48 void output(){ 49 int i,j,inzero=0,outzero=0; 50 for(i=1;i<=n;i++){ 51 indegree[i]=outdegree[i]=0; 52 } 53 for(i=1;i<=n;i++) //找连通分量入度与出度 54 for(j=1;j<=n;j++) 55 if(map[i][j] && belong[i]!=belong[j]){ 56 indegree[belong[j]]++; 57 outdegree[belong[i]]++; 58 } 59 for(i=1;i<=count;i++){ //找入度与出度为0的点 60 if(!indegree[i]) inzero++; 61 if(!outdegree[i]) outzero++; 62 } 63 64 if(count==1) //只有1个结点要特判 65 printf("1\n0\n"); 66 else 67 printf("%d\n%d\n",inzero,max(inzero,outzero)); 68 } 69 70 int main(){ 71 int i,a; 72 while(~scanf("%d",&n)){ 73 for(i=1;i<=n;i++){ 74 while(scanf("%d",&a) && a) map[i][a]=1; 75 } 76 kosaraju(); 77 output(); 78 } 79 return 0; 80 }
另一个用tarjan算法的实现
1 /* 2 tarjan 3 Memory 224K 4 Time 0MS 5 */ 6 #include <iostream> 7 using namespace std; 8 #define MAXV 110 9 #define min(a,b) (a>b?b:a) 10 #define max(a,b) (a>b?a:b) 11 12 int n,map[MAXV][MAXV],outdegree[MAXV],indegree[MAXV]; 13 int dfn[MAXV]; //第一次访问的步数 14 int low[MAXV]; //子树中最早的步数 15 int stap[MAXV],stop; //模拟栈 16 bool instack[MAXV]; //是否在栈中 17 int count; //记录连通分量的个数 18 int cnt; //记录搜索步数 19 int belong[MAXV]; //属于哪个连通分量 20 21 void init(){ 22 count=stop=cnt=0; 23 memset(instack,false,sizeof(instack)); 24 memset(map,0,sizeof(map)); 25 memset(dfn,0,sizeof(dfn)); 26 } 27 28 void tarjan(int x){ 29 int i; 30 dfn[x]=low[x]=++cnt; 31 stap[stop++]=x; 32 instack[x]=true; 33 for(i=1;i<=n;i++){ 34 if(!map[x][i]) continue; 35 if(!dfn[i]){ 36 tarjan(i); 37 low[x]=min(low[i],low[x]); 38 }else if(instack[i]) 39 low[x]=min(dfn[i],low[x]); 40 //与x相连,但是i已经被访问过,且还在栈中 41 //用子树节点更新节点第一次出现的时间 42 } 43 44 if(low[x]==dfn[x]){ 45 count++; 46 while(1){ 47 int tmp=stap[--stop]; 48 belong[tmp]=count; 49 instack[tmp]=false; 50 if(tmp==x) break; 51 } 52 } 53 } 54 55 void output(){ 56 int i,j,inzero=0,outzero=0; 57 for(i=1;i<=n;i++){ 58 indegree[i]=outdegree[i]=0; 59 } 60 for(i=1;i<=n;i++) //找连通分量入度与出度 61 for(j=1;j<=n;j++) 62 if(map[i][j] && belong[i]!=belong[j]){ 63 indegree[belong[j]]++; 64 outdegree[belong[i]]++; 65 } 66 for(i=1;i<=count;i++){ //找入度与出度为0的点 67 if(!indegree[i]) inzero++; 68 if(!outdegree[i]) outzero++; 69 } 70 71 if(count==1) //只有1个结点要特判 72 printf("1\n0\n"); 73 else 74 printf("%d\n%d\n",inzero,max(inzero,outzero)); 75 } 76 77 int main(){ 78 int i,a; 79 while(~scanf("%d",&n)){ 80 init(); 81 for(i=1;i<=n;i++){ 82 while(scanf("%d",&a) && a) map[i][a]=1; 83 } 84 for(i=1;i<=n;i++) 85 if(!dfn[i]) tarjan(i); 86 output(); 87 } 88 return 0; 89 }
什么时候能够不再这么懒惰