题目大意:
N个学校之间有单向的网络,每个学校得到一套软件后,可以通过单向网络向周边的学校传输。问题1:初始至少需要向多少个学校发放软件,使得网络内所有的学校最终都能得到软件。问题2:至少需要添加几条传输线路(边),使任意向一个学校发放软件后,经过若干次传送,网络内所有的学校最终都能得到软件。
链接http://vjudge.net/problem/viewProblem.action?id=17001
每个强连通分量内只要有任意一个学校获得一份软件就可以了,因为强连通分量内的任意两点是相互可达的。
也就是说,在一个强联通分量内的学校可以当作一个学校!
那么第一问我们所求的就是强连通分量重入度为零的点
而第二问因为两个强连通合并必然是出度为0的连接入度为0的点,所以要解决掉入度为0,和出度为0的点,所以答案是这两个的最大值(点指缩点)。
利用scc[i]=scc_cnt 来记录i这个点属于scc_cnt这个连通分量
总代码如下
#include <iostream> #include <cstdio> #include <cstring> #include <stack> using namespace std; #define N 105 int first[N],scc[N],dfn[N],low[N],degreeIn[N],degreeOut[N],k,n,tmpdfn,scc_cnt; stack<int> q; struct Path{ int y,next; }path[100005]; void init() { memset(first,-1,sizeof(first)); memset(dfn,0,sizeof(dfn)); memset(degreeIn,0,sizeof(degreeIn)); memset(degreeOut,0,sizeof(degreeOut)); memset(scc,0,sizeof(scc)); scc_cnt=0,tmpdfn=0,k=0; } void add(int x,int y) { path[k].y=y,path[k].next=first[x]; first[x]=k; k++; } void dfs(int u) { dfn[u]=low[u]=++tmpdfn; q.push(u); for(int i=first[u];i!=-1;i=path[i].next){ int v=path[i].y; if(!dfn[v]){ dfs(v); low[u]=min(low[v],low[u]); } else if(!scc[v]) low[u]=min(low[u],dfn[v]); } if(low[u]==dfn[u]){ scc_cnt++; for(;;){ int v=q.top(); q.pop(); scc[v]=scc_cnt; if(u==v) break; } } } void get_scc() { while(!q.empty()) q.pop(); for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); //cout<<"OK"<<endl; for(int i=1;i<=n;i++){ for(int j=first[i];j!=-1;j=path[j].next){ int v=path[j].y; //cout<<v<<endl; if(scc[i]!=scc[v]){ degreeOut[scc[i]]++; degreeIn[scc[v]]++; } } } } int main() { int i,a; while(~scanf("%d",&n)){ int ans1=0,ans2=0; init(); for(i=1;i<=n;i++){ while(scanf("%d",&a) && a) add(i,a); } get_scc(); //for(int i=1;i<=scc_cnt;i++) cout<<degreeIn[i]<<endl; //cout<<scc_cnt<<endl; for(int i=1;i<=scc_cnt;i++){ if(!degreeOut[i]) ans1++; if(!degreeIn[i]) ans2++; } ans1=max(ans1,ans2); //cout<<"Á¬Í¨·ÖÁ¿£º"<<cnt<<endl; if(scc_cnt>1){ printf("%d ",ans2); printf("%d ",ans1); } else printf("1 0 "); } return 0; }