题目链接:传送门
题目大意:有n个学校,每个学校给定能够传送到信息的学校,以0结束。
问:最少要选择几个学校,才能将信息传送到所有的学校,要至少加几条边,才能从任意学校都能将信息传送到所有的学校。
思路:先缩点,再找出缩点后入度为零的点的个数x,找出出度为零的点的个数y,输出x,max(x,y)即可,但是要注意,如果整个图都是一个连通图,那么就输出1 0。
第一问:我们来想缩点后的新图,如果要访问所有的点,最少的点数就是从入度为零的点出发,否则他们是无法被其他的点访问到的。
第二问:如果从入度到出度的点,这是一条边,那么从出度到入度的点也需要一条边才能满足条件(从任意学校都能将信息传送到所有的学校),那么从出度为零的点到入度为零的点最少需要max(x,y)个边。
kuangbin的更详解:传送门
代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<iostream>
#include<stack>
#include<algorithm>
using namespace std;
const int maxn=1009;
int firs[maxn],sccinq[maxn],lowpow[maxn],pre[maxn],sccnow[maxn],scc_clock,dfs_clock;
int x[maxn],y[maxn];
int N;
struct node
{
int v,nex;
} edge[maxn*maxn];
void build_edge(int u,int v)
{
edge[N]=(node)
{
v,firs[u]
};
firs[u]=N++;
}
stack<int>s;
void Trajan(int u)
{
lowpow[u]=pre[u]=++dfs_clock;
s.push(u);
for(int i=firs[u]; i!=-1; i=edge[i].nex)
{
int v=edge[i].v;
if(!pre[v])
{
Trajan(v);
lowpow[u]=min(lowpow[u],lowpow[v]);
}
else if(!sccnow[v])
lowpow[u]=min(lowpow[u],lowpow[v]);
}
if(lowpow[u]==pre[u])
{
scc_clock++;
while(1)
{
int x=s.top();
s.pop();
sccnow[x]=scc_clock;
sccinq[scc_clock]++;
if(u==x) break;
}
}
}
void find_scc(int n)
{
scc_clock=dfs_clock=0;
memset(sccinq,0,sizeof(sccinq));
memset(sccnow,0,sizeof(sccnow));
memset(lowpow,0,sizeof(lowpow));
memset(pre,0,sizeof(pre));
for(int i=1; i<=n; i++)
if(!pre[i])
Trajan(i);
}
int main()
{
int n;
while(~scanf("%d",&n))
{
memset(firs,-1,sizeof(firs));
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
N=0;
int u,v;
for(int i=1; i<=n; i++)
{
while(~scanf("%d",&v),v)
build_edge(i,v);
}
find_scc(n);
for(int i=1; i<=n; i++)
{
for(int j=firs[i]; j!=-1; j=edge[j].nex)
{
int v=edge[j].v;
if(sccnow[i]!=sccnow[v])
{
x[sccnow[i]]=y[sccnow[v]]=1;
}
}
}
int sum1=0,sum2=0;
for(int i=1; i<=scc_clock; i++)
{
if(!x[i]) sum1++;
if(!y[i]) sum2++;
}
if(sccinq[1]==n)
printf("1\n0\n");
else printf("%d\n%d\n",sum2,max(sum1,sum2));
}
return 0;
}