暑假集训每日一题 0725 (强连通分量)
Description
N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。
问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。
问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
Input
输入有多组样例,大约1000组。
每组样例第一行包含两个整数N,M(2<=N<=100),N代表学校的个数,M代表边的个数(M<N*N)
接下来M行,每行包含连个整数u,v,代表u可以向v单向发送数据。
Output
每组样例对应两行,分别是问题一和问题二的解。
Sample Input
7 8
1 2
2 3
3 1
1 4
4 5
5 6
6 4
7 6
Sample Output
2
2
分析,属于同一强连通分量的学校可以共享软件,所以可以将属于同一强连通分量的学校看成一个学校,这就是缩点,缩点后的有向图是一个有向无环图,此时需要的软件的个数就是入度为0的结点的数目。而要将所有点都连到一个强连通分量内,简单的想法就是使所有点的入度和出度都不为0.因此需添加的边数就是入度和出度中的较大者(相当于从叶子到树根连边)。
需要注意的是,有一种特殊情况,那就是所有点本来就属于同一个强连通分量内。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> #define MAX(a,b) ((a)>(b)?(a):(b)) #define N 101 int g[N][N]; int n,m; int ans1,ans2; int cnt; int vis[N],dfn[N],id[N]; int din[N],dout[N]; void dfs(int u) { int v; vis[u]=1; for(v=1;v<=n;v++) { if(g[u][v] && !vis[v]) dfs(v); } dfn[cnt++]=u; } void rdfs(int u) { int v; vis[u]=1; id[u]=cnt; for(v=1;v<=n;v++) { if(g[v][u] && !vis[v]) rdfs(v); } } void solve() { int i,j,t; memset(vis,0,sizeof(vis)); cnt=0; for(i=1;i<=n;i++) { if(!vis[i]) dfs(i); } memset(vis,0,sizeof(vis)); cnt=0; for(t=n-1;t>=0;t--) { i=dfn[t]; if(!vis[i]) rdfs(i),cnt++; } memset(din,0,sizeof(din)); memset(dout,0,sizeof(dout)); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(id[i]!=id[j]&&g[i][j]) dout[id[i]]++,din[id[j]]++; } } ans1=ans2=0; for(i=0;i<cnt;i++) { if(!din[i]) ans1++; if(!dout[i]) ans2++; } if(cnt==1) printf("1\n0\n"); else printf("%d\n%d\n",ans1,MAX(ans1,ans2)); } int main() { int u,v; while(~scanf("%d%d",&n,&m)) { memset(g,0,sizeof(g)); while(m--) { scanf("%d%d",&u,&v); g[u][v]=1; } solve(); } return 0; }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <stdio.h> #include <string.h> #define MIN(a,b) ((a)<(b)?(a):(b)) #define MAX(a,b) ((a)>(b)?(a):(b)) #define N 101 int g[N][N]; int n,m; int cnt,num; int dfn[N],low[N]; int stk[N],ins[N],top; int id[N]; int din[N],dout[N]; void dfs(int u) { int v,tmp; dfn[u]=low[u]=cnt++; stk[top++]=u; ins[u]=1; for(v=1;v<=n;v++) if(g[u][v]) { if(dfn[v]==-1) dfs(v),low[u]=MIN(low[u],low[v]); else if(ins[v]) low[u]=MIN(low[u],dfn[v]); } if(dfn[u]==low[u]) { do { tmp=stk[--top]; id[tmp]=num; ins[tmp]=0; }while(tmp!=u); num++; } } void solve() { int i,j,ans1,ans2; cnt=num=top=0; memset(ins,0,sizeof(ins)); memset(dfn,-1,sizeof(dfn)); for(i=1;i<=n;i++) { if(dfn[i]==-1) dfs(i); } memset(din,0,sizeof(din)); memset(dout,0,sizeof(dout)); for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(id[i]!=id[j] && g[i][j]) dout[id[i]]++,din[id[j]]++; } } ans1=ans2=0; for(i=0;i<num;i++) { if(!din[i]) ans1++; if(!dout[i]) ans2++; } if(num==1) printf("1\n0\n"); else printf("%d\n%d\n",ans1,MAX(ans1,ans2)); } int main() { int u,v; while(~scanf("%d%d",&n,&m)) { memset(g,0,sizeof(g)); while(m--) { scanf("%d%d",&u,&v); g[u][v]=1; } solve(); } return 0; }