题意:
一幅有向图 第一问选择最少的点当起点 使得从某一起点出发可以到图中任何一点 第二问最少加多少边可以使任意两点可达
思路:
久违的强连通缩点题
首先tarjan
然后对强连通分量缩成的点统计入度和出度
最后
对于第一问起点数量即入度为0点数量(因为它们不可以被别人到达)
第一问特殊情况 图中任意两点可达 那么你最少也要选一个起点(要不没地方开始走啊…)
对于第二问答案即max(入度为0点,出度为0点) 简单的解释一下就是最少连边的方案就是一个出度为0点连向一个入度为0点以此构成环 如果某一种点多出来几个 就再多连几条边
第二问特殊情况 图中任意两点可达 这时明显答案为0 但是通过求max的结果是1要修正一下
代码:
/*
ID: housera1
PROG: schlnet
LANG: C++
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 110
int n,cnt,idx,top;
int maz[N][N],low[N],dfn[N],st[N],inst[N],belong[N],in[N],out[N];
void tarjan(int u)
{
int i,v;
dfn[u]=low[u]=++idx;
st[top++]=u;
inst[u]=1;
for(i=1;i<=n;i++)
{
if(maz[u][i])
{
if(!dfn[i])
{
tarjan(i);
low[u]=min(low[u],low[i]);
}
else if(inst[i]) low[u]=min(low[u],dfn[i]);
}
}
if(dfn[u]==low[u])
{
cnt++;
do
{
v=st[--top];
belong[v]=cnt;
inst[v]=0;
}while(v!=u);
}
}
int main()
{
int Debug=0;
if(!Debug)
{
freopen("schlnet.in","r",stdin);
freopen("schlnet.out","w",stdout);
}
int i,j,u,v,ans;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
while(~scanf("%d",&j))
{
if(!j) break;
maz[i][j]=1;
}
}
for(i=1;i<=n;i++)
{
if(!dfn[i]) tarjan(i);
}
//printf("%d\n",cnt);
//for(i=1;i<=n;i++) printf("%d %d\n",i,belong[i]);
for(i=1;i<=n;i++)
{
u=belong[i];
for(j=1;j<=n;j++)
{
v=belong[j];
if(maz[i][j]&&u!=v)
{
out[u]++;
in[v]++;
}
}
}
//for(i=1;i<=cnt;i++) printf("%d in %d out %d\n",i,in[i],out[i]);
for(i=1,ans=0;i<=cnt;i++)
{
if(!in[i]) ans++;
}
ans=max(ans,1);
printf("%d\n",ans);
for(i=1,u=0,v=0;i<=cnt;i++)
{
if(!in[i]) u++;
if(!out[i]) v++;
}
ans=max(u,v);
if(cnt==1) ans=0;
printf("%d\n",ans);
return 0;
}