poj 1236(强连通分量分解模板题)

传送门

 

题意:

  N(2<N<100)个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。

  问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。

  问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。

 

从题意中抽象出的算法模型:

  给定一个有向图,求:

  1) 至少要选几个顶点,才能做到从这些顶点出发,可以到达全部顶点。

  2) 至少要加多少条边,才能使得从任何一个顶点出发,都能到达全部顶点。

 

题解:

  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的点,添加一条出边,这需要加n条边。

  若 m <= n,则

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

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

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

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

以上解析来源于bin巨%%%%%%

AC代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<vector>
 5 using namespace std;
 6 #define pb push_back
 7 #define mem(a,b) memset(a,b,sizeof a)
 8 const int maxn=110;//最大的节点个数,一般是 1e5 级别的
 9 
10 int scc[maxn];//所属强连通分量的拓扑排序
11 bool vis[maxn];//vis[u] : dfs中判断节点u是否被访问过
12 vector<int >vs;//后序遍历顺序的顶点列表
13 vector<int >edge[maxn],redge[maxn];//边、反边
14 
15 void addEdge(int u,int v)
16 {
17     edge[u].pb(v);
18     redge[v].pb(u);
19 }
20 void Dfs(int u)//第一次dfs,后序遍历标记,越靠近叶子结点标号越小
21 {
22     vis[u]=true;
23     for(int i=0;i < edge[u].size();++i)
24     {
25         int to=edge[u][i];
26         if(!vis[to])
27             Dfs(to);
28     }
29     vs.pb(u);
30 }
31 void rDfs(int u,int sccId)//反向dfs,利用反向图,求出强连通分量个数
32 {
33     vis[u]=true;
34     scc[u]=sccId;
35     for(int i=0;i < redge[u].size();++i)
36     {
37         int to=redge[u][i];
38         if(!vis[to])
39             rDfs(to,sccId);
40     }
41 }
42 int Scc(int maxV)
43 {
44     mem(vis,false);
45     vs.clear();
46     for(int i=1;i <= maxV;++i)
47         if(!vis[i])
48             Dfs(i);
49     mem(vis,false);
50     int sccId=0;//DAG节点个数
51     for(int i=vs.size()-1;i >= 0;--i)
52     {
53         int to=vs[i];
54         if(!vis[to])
55         {
56             sccId++;
57             rDfs(to,sccId);
58         }
59     }
60     return sccId;//返回强连通分量的个数
61 }
62 int main()
63 {
64     int N;
65     scanf("%d",&N);
66     for(int i=1;i <= N;++i)
67     {
68         int v;
69         while(scanf("%d",&v) && v)
70             addEdge(i,v);
71     }
72     int sccId=Scc(N);
73     int in[maxn];
74     int out[maxn];
75     mem(in,0);
76     mem(out,0);
77 
78     for(int i=1;i <= N;++i)
79         for(int j=0;j < edge[i].size();++j)
80         {
81             int to=edge[i][j];
82             if(scc[i] != scc[to])
83                 out[scc[i]]++,in[scc[to]]++;
84         }
85     int zeroIn=0;
86     int zeroOut=0;
87     for(int i=1;i <= sccId;++i)
88     {
89         zeroIn += (in[i] == 0 ? 1:0);
90         zeroOut += (out[i] == 0 ? 1:0);
91     }
92     printf("%d\n",zeroIn);
93     if(sccId == 1)
94         printf("0\n");
95     else
96         printf("%d\n",max(zeroIn,zeroOut));
97 }
View Code

 


分割线2019.4.27

半年后的我,代码风格:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<vector>
  5 using namespace std;
  6 #define pb(x) push_back(x)
  7 #define mem(a,b) memset(a,b,sizeof(a))
  8 const int maxn=100+50;
  9 
 10 int n;
 11 int num;
 12 int head[maxn];
 13 struct Edge
 14 {
 15     int to;
 16     int next;
 17 }G[maxn*maxn<<1];
 18 void addEdge(int u,int v)
 19 {
 20     G[num]={v,head[u]};
 21     head[u]=num++;
 22 }
 23 struct SCC
 24 {
 25     int col[maxn];
 26     int in[maxn];
 27     int out[maxn];
 28     bool vis[maxn];
 29     vector<int >vs;
 30     void DFS(int u)
 31     {
 32         vis[u]=true;
 33         for(int i=head[u];~i;i=G[i].next)
 34         {
 35             int v=G[i].to;
 36             if(vis[v] || (i&1))
 37                 continue;
 38             DFS(v);
 39         }
 40         vs.pb(u);
 41     }
 42     void RDFS(int u,int k)
 43     {
 44         col[u]=k;
 45         vis[u]=true;
 46         for(int i=head[u];~i;i=G[i].next)
 47         {
 48             int v=G[i].to;
 49             if(vis[v] || !(i&1))
 50                 continue;
 51             RDFS(v,k);
 52         }
 53     }
 54     int scc()
 55     {
 56         mem(vis,false);
 57         vs.clear();
 58         for(int i=1;i <= n;++i)
 59             if(!vis[i])
 60                 DFS(i);
 61 
 62         int k=0;
 63         mem(vis,false);
 64         for(int i=vs.size()-1;i >= 0;--i)
 65             if(!vis[vs[i]])
 66                 RDFS(vs[i],++k);
 67 
 68         mem(in,0);
 69         mem(out,0);
 70         for(int u=1;u <= n;++u)
 71         {
 72             for(int i=head[u];~i;i=G[i].next)
 73             {
 74                 int v=G[i].to;
 75                 if(i&1)
 76                     continue;
 77                 if(col[u] != col[v])
 78                 {
 79                     out[col[u]]++;///col[u]:出度++
 80                     in[col[v]]++;///col[v]:入度++
 81                 }
 82             }
 83         }
 84         return k;
 85     }
 86 }_scc;
 87 void Solve()
 88 {
 89     int k=_scc.scc();
 90 
 91     int zeroIn=0;
 92     int zeroOut=0;
 93     for(int i=1;i <= k;++i)
 94     {
 95         if(!_scc.in[i])
 96             zeroIn++;
 97         if(!_scc.out[i])
 98             zeroOut++;
 99     }
100     printf("%d\n%d\n",zeroIn,k == 1 ? 0:max(zeroIn,zeroOut));
101 }
102 void Init()
103 {
104     num=0;
105     mem(head,-1);
106 }
107 int main()
108 {
109 //    freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin);
110     while(~scanf("%d",&n))
111     {
112         Init();
113         for(int i=1;i <= n;++i)
114         {
115             int v;
116             while(scanf("%d",&v) && v)
117             {
118                 addEdge(i,v);
119                 addEdge(v,i);
120             }
121         }
122         Solve();
123     }
124     return 0;
125 }
View Code

 

posted @ 2018-10-03 19:59  HHHyacinth  阅读(474)  评论(0编辑  收藏  举报