暑假集训每日一题 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.因此需添加的边数就是入度和出度中的较大者(相当于从叶子到树根连边)。
需要注意的是,有一种特殊情况,那就是所有点本来就属于同一个强连通分量内。

#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; }

#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; }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题
· 为什么说在企业级应用开发中,后端往往是效率杀手?
· 用 C# 插值字符串处理器写一个 sscanf
· Java 中堆内存和栈内存上的数据分布和特点
· 开发中对象命名的一点思考
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析
· 重磅发布!DeepSeek 微调秘籍揭秘,一键解锁升级版全家桶,AI 玩家必备神器!