//Tarjan算法 缩点变形 #include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<stack> #include<algorithm> using namespace std; const int maxn = 100 + 15; vector<int> Grape[maxn]; bool vis[maxn]; int dfn[maxn],low[maxn],arr[maxn],chudu[maxn],rudu[maxn]; int total,cnt; stack<int> s; void tarjan(int u) { dfn[u] = low[u] = ++total; s.push(u); vis[u] = true;//u节点入栈 for(int i=0;i!=Grape[u].size();++i) { int v = Grape[u][i]; if(!dfn[v]) { tarjan(v); low[u] = min(low[u],low[v]); }else if(vis[v]) low[u] = min(low[u],dfn[v]); } if(dfn[u]==low[u])//找到连通分图根节点 { ++cnt;//标记连通分量的个数 int v; do{ v = s.top(); s.pop(); vis[v] = false;//出栈 arr[v] = cnt; }while(v!=u); } } int main() { int n; while(cin>>n) { for(int i=1;i<=n;++i) Grape[i].clear(); int v; for(int i=1;i<=n;++i) { while(cin>>v&&v) Grape[i].push_back(v); } memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(arr,0,sizeof(arr)); memset(chudu,0,sizeof(chudu)); memset(rudu,0,sizeof(rudu)); total = 0,cnt = 0; for(int v=1;v<=n;++v) if(!dfn[v]) tarjan(v); //统计出入度 int anschu = 0,ansru = 0; for(int u=1;u<=n;++u) { for(int i=0;i!=Grape[u].size();++i) { int v = Grape[u][i]; if(arr[u]!=arr[v]) { ++chudu[arr[u]]; ++rudu[arr[v]]; } } } for(int v=1;v<=cnt;++v) { if(!chudu[v]) ++anschu; if(!rudu[v]) ++ansru; } if(cnt==1) { cout<<1<<endl; cout<<0<<endl; continue; }//只存在一个强连通分量时,不需加边 cout<<ansru<<endl; cout<<max(anschu,ansru)<<endl;//入度要加边,出度也要加边 } }
//一些细节需要注意