链接:https://cn.vjudge.net/problem/POJ-1236
题意:n个点,每个点都指向一些点,编号从0开始,问有多少入度为0的点,并且需要加多少边才能使得n个点两两互达。
思路:第一个答案就是缩点后入度为0的点的个数,第二个答案为入度为0的点的个数和出度为0的点的个数取最大值。(要想两两互达,那么肯定没有入度为0和出度为0的点,那么就将出度为0的点指向入度为0的点即可。)
#include <cstdio>
#include <algorithm>
#include <cstring>
#define ll long long
using namespace std;
const int N = 110;
const int M = 1e4+10;
int n,x,head[N],cnt,color[N],dfn[N],low[N],id,sta[N],top,num,in[N],out[N];
bool vis[N],book[N][N];
struct Edge
{
int to,nxt;
}g[M];
void Init()
{
id=top=num=cnt=0;
for(int i=1;i<=n;i++)
in[i]=out[i]=dfn[i]=vis[i]=0,head[i]=-1;
memset(book,0,sizeof(book));
return ;
}
void add(int u,int v)
{
g[cnt].to=v; g[cnt].nxt=head[u]; head[u]=cnt++;
return ;
}
void tarjan(int u)
{
int v;
dfn[u]=low[u]=++id; sta[++top]=u; vis[u]=1;
for(int i=head[u];i!=-1;i=g[i].nxt)
{
v=g[i].to;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(vis[v])
low[u]=min(low[u],low[v]);
}
if(dfn[u]==low[u])
{
color[u]=++num;
while(sta[top]!=u)
color[sta[top]]=num,vis[sta[top--]]=0;
vis[sta[top--]]=0;
}
return ;
}
int main(void)
{
int ans1,ans2,u,v;
while(~scanf("%d",&n))
{
Init();
for(int i=1;i<=n;i++)
{
while(~scanf("%d",&x)&&x)
add(i,x);
}
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
if(num==1)
{
printf("1\n0\n");
continue;
}
for(u=1;u<=n;u++)
for(int i=head[u];i!=-1;i=g[i].nxt)
{
v=g[i].to;
if(color[u]==color[v]||book[color[u]][color[v]]) continue;
book[color[u]][color[v]]=1;
out[color[u]]++; in[color[v]]++;
}
ans1=ans2=0;
for(int i=1;i<=num;i++)
{
if(!in[i]) ans1++;
if(!out[i]) ans2++;
}
printf("%d\n%d\n",ans1,max(ans2,ans1));
}
return 0;
}