【强连通分量缩点】poj 1236 Network of Schools

poj.org/problem?id=1236

【题意】

  • 给定一个有向图,求:
  • (1)至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点
  • (2)至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点

【思路】

  • (1)强连通分量缩点后形成一个有向无环图,只要选择入度为0的顶点,其他顶点都可以被到达
  • (2)等价于一个有向无环图加最少加多少条边能够变成一个强连通图,取出度为0的点的个数和入度为0的点的个数的max,因为出度为0的点要加一条出边,入度为0的点要加一条入边
  • (2)特判特殊情况:强连通分量只有一个,这时虽然入度为0和出度为0的点都是一个,但不需要加边

【AC】

  1 //#include<bits/stdc++.h>
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<string>
  5 #include<cstring>
  6 #include<algorithm>
  7 #include<cmath>
  8 using namespace std;
  9 typedef long long ll;
 10 int n;
 11 const int maxm=10002;
 12 const int maxn=1e2+2;
 13 struct edge
 14 {
 15     int to;
 16     int nxt;
 17 }e[maxm];
 18 int head[maxn],tot;
 19 int dfn[maxn],low[maxn],id;
 20 int S[maxn],top;
 21 int num,belong[maxn];
 22 bool vis[maxn];
 23 bool in[maxn];
 24 bool out[maxn];
 25 void init()
 26 {
 27     memset(head,-1,sizeof(head));
 28     tot=0;
 29     id=0;
 30     top=0;
 31     num=0;
 32     memset(dfn,0,sizeof(dfn));
 33     memset(low,0,sizeof(low));
 34     memset(S,0,sizeof(S));
 35     memset(vis,false,sizeof(vis));
 36     memset(belong,0,sizeof(belong));
 37     memset(in,false,sizeof(in));
 38     memset(out,false,sizeof(out));    
 39 }
 40 void addedge(int u,int v)
 41 {
 42     e[tot].to=v;
 43     e[tot].nxt=head[u];
 44     head[u]=tot++;
 45 }
 46 void tarjan(int u)
 47 {
 48     dfn[u]=low[u]=++id;
 49     S[++top]=u;
 50     vis[u]=true;
 51     for(int i=head[u];i!=-1;i=e[i].nxt)
 52     {
 53         int v=e[i].to;
 54         if(!dfn[v])
 55         {
 56             tarjan(v);
 57             low[u]=min(low[u],low[v]);
 58         }
 59         else if(vis[v]) low[u]=min(low[u],dfn[v]);
 60     }
 61     if(dfn[u]==low[u])
 62     {
 63         num++;
 64         while(1)
 65         {
 66             belong[S[top]]=num;
 67             vis[S[top]]=false;
 68             if(S[top--]==u) break;
 69         }
 70     }
 71     
 72 }
 73 
 74 int main()
 75 {
 76     while(~scanf("%d",&n))
 77     {
 78         init();
 79         for(int i=1;i<=n;i++)
 80         {
 81             int x;
 82             while(1)
 83             {
 84                 scanf("%d",&x);
 85                 if(x==0) break;
 86                 addedge(i,x);
 87             }
 88         }
 89         for(int i=1;i<=n;i++)
 90         {
 91             if(!dfn[i]) tarjan(i);
 92         }
 93         for(int u=1;u<=n;u++)
 94         {
 95             for(int i=head[u];i!=-1;i=e[i].nxt)
 96             {
 97                 int v=e[i].to;
 98                 if(belong[u]==belong[v]) continue;
 99                 in[belong[v]]=true;
100                 out[belong[u]]=true;
101             }
102         }
103         int ans1=0;
104         int ans2=0;
105         for(int i=1;i<=num;i++)
106         {
107             if(!in[i]) ans1++;
108             if(!out[i]) ans2++; 
109         }
110         int ans=max(ans1,ans2);
111         if(num==1) ans=0;
112         printf("%d\n%d\n",ans1,ans);
113     }
114     return 0;
115 }
View Code

【大佬博客】

www.cnblogs.com/kuangbin/archive/2011/08/07/2130277.html

解题思路:

—        1. 求出所有强连通分量

—        2. 每个强连通分量缩成一点,则形成一个有向无环图DAG

—        3. DAG上面有多少个入度为0的顶点,问题1的答案就是多少

在DAG上要加几条边,才能使得DAG变成强连通的,问题2的答案就是多少

加边的方法:

要为每个入度为0的点添加入边,为每个出度为0的点添加出边

假定有 n 个入度为0的点,m个出度为0的点,如何加边?

把所有入度为0的点编号 0,1,2,3,4 ....N -1

每次为一个编号为i的入度0点可达的出度0点,添加一条出边,连到编号为(i+1)%N 的那个出度0点,

这需要加n条边

若 m <= n,则

加了这n条边后,已经没有入度0点,则问题解决,一共加了n条边

若 m > n,则还有m-n个入度0点,则从这些点以外任取一点,和这些点都连上边,即可,这还需加m-n条边。

所以,max(m,n)就是第二个问题的解

此外:当只有一个强连通分支的时候,就是缩点后只有一个点,虽然入度出度为0的都有一个,但是实际上不需要增加清单的项了,所以答案是1,0

posted @ 2017-08-27 22:16  shulin15  阅读(218)  评论(0编辑  收藏  举报