【题目】http://poj.org/problem?id=1236
【题意】给出几个点和有向边,问1:取其中几个点能到达所有的点;2:最少加几条边使其成为强连通图。
【思路】用tarjan算法将强连通分支缩点;设缩点之后入度为0的个数为m,出度为0的个数为n,1的答案为m,2的答案为max(m,n)
【题解参考】https://www.cnblogs.com/kuangbin/p/3182611.html
【tarjan算法】https://blog.csdn.net/qq_34374664/article/details/77488976
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=505;
int n;
int mp[M][M];
bool inthestack[M];
int LOW[M];
int DFN[M],cnt;
int Stack[M],si;
int scc,Belong[M];
void Tarjan(int x)
{
LOW[x]=DFN[x]=++cnt;
Stack[si++]=x;
inthestack[x]=1;
for(int i=1; i<=n; i++)
{
if(mp[x][i]>0)
{
if(DFN[i]==0)
{
Tarjan(i);
LOW[x]=min(LOW[x],LOW[i]);
}
else if(DFN[i]!=0&&inthestack[i])
{
LOW[x]=min(LOW[x],DFN[i]);
}
}
}int v;
if(LOW[x]==DFN[x])
{scc++;
do
{
v= Stack[--si];
Belong[v] = scc;
inthestack[v] = 0;
}
while( v!= x);
}
}
void init()
{
memset(mp,0,sizeof(mp));
memset(inthestack,0,sizeof(inthestack));
memset(DFN,0,sizeof(DFN));
memset(LOW,0,sizeof(LOW));
cnt=si=0;
}
int main()
{
scanf("%d",&n);
init();
for(int i=1; i<=n; i++)
{
int t;
while(~scanf("%d",&t)&&t)
{
mp[i][t]=1;
}
}
scc=0;
for(int i=1;i<=n;i++)
if(!DFN[i])Tarjan(i);
// for(int i=1;i<=n;i++)printf("%d ",LOW[i]);
// printf("\n");
//
if(scc == 1)
{
printf("1\n0\n");
return 0;
}
int in[M],out[M];
for(int i = 1;i <= scc;i++)
in[i] = out[i] = 0;
for(int u = 1;u <= n;u++)
{
for(int v = 1;v <= n;v++)
{
if(u==v)continue;
if(mp[u][v]==0)continue;
if(Belong[u] != Belong[v])
{
in[Belong[v]]++;
out[Belong[u]]++;
}
}
}
int ans1=0,ans2=0;
for(int i = 1;i <= scc;i++)
{
if(in[i]==0)ans1++;
if(out[i]==0)ans2++;
}
printf("%d\n%d\n",ans1,max(ans1,ans2));
}