题目大意:有N个学校和一些有向边将它们连结,求:
1.最少需要向几个学校发放软件,使得他们中的每一个学校最终都能够获得软件。
2.最少需要增加几条有向边使得可以从任意一个学校发放软件,使得每一个学校最终都能够获得软件。
分析:
1.缩点以后,找出入度为0的点的个数即可(因为没人可以给他们软件)。
2.缩点以后,答案即是max(入度为0的点,出度为0的点)。因为只要另每一对入度为0和出度为0的点相连接即可。
第二个问题也有无向图的版本,那样的话处理方法是:缩点以后,找出那些度为1的点,个数为cnt,向上整除2即可(即答案为(cnt+1)/2)。
代码如下:
1 #include <stdio.h> 2 #include <stack> 3 #include <algorithm> 4 #include <string.h> 5 #include <vector> 6 using namespace std; 7 8 const int N = 100+5; 9 10 stack<int> S; 11 int scc_cnt; //强连通分量的个数 12 int dfs_clock; //访问到该节点的时间戳 13 int belong[N]; //belong[i]表示i节点所属于第几个强连通分量 14 int dfn[N]; //表示第i个节点被访问的时间 15 int low[N]; //表示第i个节点的子节点所能访问到的最小的dfn值 16 vector<int> G[N]; 17 int in[100+5],out[100+5]; 18 19 void dfs(int u) 20 { 21 dfn[u] = low[u] = ++dfs_clock; 22 S.push(u); 23 for(int i=0;i<G[u].size();i++) 24 { 25 int v = G[u][i]; 26 if(!dfn[v]) 27 { 28 dfs(v); 29 low[u] = min(low[u],low[v]); 30 } 31 else if(!belong[v]) 32 { 33 low[u] = min(low[u],low[v]); 34 } 35 } 36 if(low[u]==dfn[u]) 37 { 38 scc_cnt++; 39 for(;;) 40 { 41 int x = S.top();S.pop(); 42 belong[x] = scc_cnt; 43 if(x==u) break; 44 } 45 } 46 } 47 48 void scc(int n) 49 { 50 memset(dfn,0,sizeof(dfn)); 51 memset(belong,0,sizeof(belong)); 52 dfs_clock = scc_cnt = 0; 53 for(int i=1;i<=n;i++) 54 { 55 if(!dfn[i]) dfs(i); 56 } 57 } 58 59 int main() 60 { 61 int n; 62 while(scanf("%d",&n)==1) 63 { 64 memset(in,0,sizeof(in)); 65 memset(out,0,sizeof(out)); 66 for(int i=1;i<=n;i++) G[i].clear(); 67 for(int i=1;i<=n;i++) 68 { 69 int to; 70 while(scanf("%d",&to)==1&&to) 71 { 72 G[i].push_back(to); 73 } 74 } 75 76 scc(n); 77 if(scc_cnt==1) 78 { 79 puts("1\n0"); 80 continue; 81 } 82 for(int i=1;i<=n;i++) 83 { 84 for(int j=0;j<G[i].size();j++) 85 { 86 int v = G[i][j]; 87 if(belong[i]==belong[v]) continue; 88 in[belong[v]]++; 89 out[belong[i]]++; 90 } 91 } 92 int incnt=0,outcnt=0; 93 for(int i=1;i<=scc_cnt;i++) 94 { 95 if(!in[i]) incnt++; 96 if(!out[i]) outcnt++; 97 } 98 printf("%d\n%d\n",incnt,max(incnt,outcnt)); 99 } 100 return 0; 101 }
时间: 2024-10-21 15:57:34