POJ1236
//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;//入度要加边,出度也要加边
}
}
//一些细节需要注意
不怕万人阻挡,只怕自己投降。
posted on 2019-09-23 18:57 chengyulala 阅读(92) 评论(0) 收藏 举报
浙公网安备 33010602011771号