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 }
分割线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 }