这道题就是tarjan的简单应用
上面是tarjan算法的详解
总结一下精华就是寻找循环点 ,利用栈和深搜寻找循环点
搜索的过程中如果找到某点,并且点在栈中,那么说明找到一个循环了,那么这个循环就是栈中点前面的所有点
比如:栈中元素从底到头为 :5 3 6 4 1 然后深搜的时候又搜到了3,这时候说明 6 4 1 3 是一个连通分量
因为6 4 1 3 必定是一个环 (这里的环必须是可以从头走到尾的环)
回归到这一题,虽然是一个简单应用,但是其实也并不好想
1.第一问,选择最少的点可以遍历这N个点。原来想着,这还不简单,算一下所有点的出度和入度
入度为零,说明没有点可以到达这点,需要的点数加一。简单!!
但是发现,如果是一个环,那么输出答案是0,但是实际答案是1,如果是两个环,输出答案应该是2
只要带环,这样就不成立的
但是发现输出的答案和环的个数有关,此处的环即一个连通分量,由于每个连通分量中任意两点可以互达
所以现在看每个连通分量是否连接,那么把每一个连通分量当做一个点。这时候再计算每一个点的入度(此时的情况中不会包括环了),最后数一数入度为0的点的个数,那么就是答案
2.第二问,用最少的边将这个图变为连通图,即每两个点两两互达
对于这个图,想让两两互达,最少的情况就是把其连成环,没有比这更少的情况了
例如,对于四个点 1 2 3 4 让其连成环这样让其两两相连用的边数最少
那么对于每一个连通分量,其已经连环,那么看成一个点就OK
然后这样,每个点目前都为成环,成为环的必要条件就是没有入度和出度为0的点
这样求一下入度和出度, 找两者最大的输出,这样可以保证连接后入度和出度没有为0的点
这样就可以连成环,成为连通图了
注意!!!当点为1的时候要特判!
#include<iostream>
#include<stdio.h>
#include<queue>
#include<string.h>
using namespace std;
const int maxn=100+10;
int line[maxn];
int dfn[maxn],low[maxn],color[maxn];
int in[maxn],out[maxn];//记录入度和出度
bool vis[maxn];
int e[maxn][maxn];
int dfs_num,col_num,top;
int ans=0;
int n;
void Tarjan(int x)
{
dfn[x]=++dfs_num;
low[x]=dfs_num ;
vis[x]=1;//标记一下,表示已经入栈
line [++top] = x ;//入栈
for(int i=1 ; i<=n; i++)
{
if(e[x][i]==0)
continue;
int temp=i;
if(!dfn[temp])
{
Tarjan(temp) ;
low[x]=min(low[x],low[temp]) ;
}
else if(vis[temp])
low[x]=min(low[ x ],dfn[temp]) ;
}
if(dfn[x]==low[x]) //构成强连通分量
{
vis[x]=false ;
color[x]=++col_num ;//染色
while(line[top]!=x) //清空
{
color[line[top]]=col_num ;
vis [line[top--]]=false ;
}
top--;
}
}
void slove()
{
memset(dfn,0,sizeof(dfn));
memset(vis,0,sizeof(vis));
memset(low,0,sizeof(low));
memset(color,0,sizeof(color));
dfs_num=0,col_num=0,top=0;
for(int i=1; i<=n; i++)
{
if(dfn[i]==0)
{
Tarjan(i);
}
}
}
int main()
{
scanf("%d",&n);
int m;
memset(e,0,sizeof(e));
for(int i=1; i<=n; i++)
{
int num=0;
while(~scanf("%d",&m)&&m)
{
e[i][m]=1;
}
}
slove();
// for(int i=1; i<=n; i++)
// {
// for(int j=1; j<=n; j++)
// {
// printf("%d ",e[i][j]);
// }
// printf("\n");
// }
// for(int i=1; i<=n; i++)
// {
// printf("%d ",color[i]);
// }
// printf("\n");
memset(in,0,sizeof(in));
memset(out,0,sizeof(out));
int in_num=0,out_num=0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=n; j++)
{
if(e[i][j]&&color[i]!=color[j])
{
in[color[j]]++;
out[color[i]]++;
}
}
}
for(int i=1; i<=col_num; i++)
{
if(!in[i])in_num++;
if(!out[i])out_num++;
}
if(col_num==1)
printf("1\n0\n");
else
printf("%d\n%d\n",in_num,max(in_num,out_num));
}