题目大意: 给出一个有向图,问1: 至少从几个点出发,可以遍历所有的点。 2: 至少添几条边可以可以是图强连通。
将图中的强连通分量缩点后,成一个 DAG(有向无环图); 图中 入度为0的点是无法通过其他点到达的,且图中从所有入度为0的点出发定能到达图中所有的点,因为其他点入度不为0。所以第一问答案就是入度为0点的个数。
2: 及入度为0点a,出度为0点b . 强连通图是由若干个环组成的。所以对于入度为0 一条路径的重点必然是出度为0,将入度为0点和出度为0点链接起来,对于剩余的出度为0 或者入度为0的点,我们需要再加边将其加入图中的环内。 ans= max(a,b);明显 ans1>=1,ans2>=0,当图为一个强连通时要特判。
#include<stack>
#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
using namespace std;
const int maxn = 105*2;
int lowlink[maxn];
int pre[maxn];
int sccno[maxn];
stack<int>s;
vector<int>g[maxn];
int n;
int G[maxn][maxn];
int in[maxn];
int out[maxn];
int scc_cnt = 0;
int dfs_clock=0;
void dfs(int u)
{
pre[u] = lowlink[u] = ++dfs_clock;
s.push(u);
for(int i = 0; i<g[u].size(); i++)
{
int v = g[u][i];
if(!pre[v])
{
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}
else if(!sccno[v])
{
lowlink[u] = min(lowlink[u],pre[v]);
}
}
if(lowlink[u]==pre[u])
{
scc_cnt++;
for(;;)
{
int x = s.top();
s.pop();
sccno[x] = scc_cnt;
if(x==u)break;
}
}
}
void find_scc(int n)
{
dfs_clock = scc_cnt = 0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
for(int i = 1; i<=n; i++)
{
if(!pre[i])dfs(i) ;
}
}
void work()
{
memset(G,0,sizeof(G));
while(!s.empty())s.pop();
find_scc(n);
for(int i = 1; i<=n; i++)
{
for(int j = 0; j<g[i].size(); j++)
{
int v = g[i][j];
if(sccno[i]!=sccno[v])
{
G[sccno[i]][sccno[v]] = 1;
}
}
}
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
for(int i = 1; i<=n; i++)
{
for(int j = 1; j<=n; j++)
{
if(G[i][j])
{
in[j]++;
out[i]++;
}
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
int x;
for(int i = 1; i<=n; i++)
g[i].clear();
for(int i = 1; i<=n; i++)
{
while(1)
{
scanf("%d",&x);
if(!x)break;
g[i].push_back(x);
}
}
work();
int ans1 = 0;
int ans2 = 0;
for(int i = 1; i<=scc_cnt; i++)
{
if(in[i]==0)
ans1++;
if(out[i]==0)
ans2++;
}
if(scc_cnt==1)
{
printf("1\n0\n");
continue;
}
cout<<ans1<<"\n"<<max(ans1,ans2)<<endl;
}
return 0;
}