题目链接:
题意:
给出一幅有向图,给出图中所有的边。问:
1.要遍历完图的所有顶点,需要遍历多少次(可以从不同起点)
2.要想只遍历一次,经过图中所有顶点(强连通)最少 需要增加多少条边
题解思路:
首先就是缩点重新建图
对于问题1:
要遍历整个图,只需要从图的所有起点开始遍历
这里图中的起点就是入度为0的点,起点个数即为遍历次数。
对于问题2:
要想使原图成为强连通图
需要保证图中没有入度为0或出度为0的顶点;
而每增加一条边 的 最优连接策略 是 从一个出度为0的顶点到一个入度为0的顶点
这样 需要增加的最少边数即为 入度为0的顶点个数 和 出度为0的顶点个数 之间的最大值
另外需要注意图中只有一个强连通分量的特殊情况(重建图后只有一个顶点):
需要的遍历次数为1
需要的最少边数为0
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 105
using namespace std;
struct node{
int to,next;
}edge[maxn*maxn];
int head[maxn];
int s;
int dfn[maxn],low[maxn],num;
int insta[maxn],sta[maxn],top;
int belong[maxn],block;
void init()
{
memset(head,-1,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(insta,0,sizeof(insta));
memset(belong,0,sizeof(belong));
top=num=s=block=0;
}
void Tarjan(int u,int pre)
{
dfn[u]=low[u]=++num;
insta[u]=1;
sta[top++]=u;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v])
{
Tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else if(insta[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u])
{
int d=-1;
block++;
while(d!=u)
{
d=sta[--top];
insta[d]=0;
belong[d]=block;
}
}
}
void rebuild(int n)
{
int in[maxn]={0};
int out[maxn]={0};
int u,v;
for(int i=1;i<=n;i++)
{
u=belong[i];
for(int j=head[i];j!=-1;j=edge[j].next)
{
v=edge[j].to;
v=belong[v];
if(u!=v)
{
in[v]++;
out[u]++;
}
}
}
int s1=0,s2=0;
for(int i=1;i<=block;i++)
{
if(!in[i])
s1++;
if(!out[i])
s2++;
}
if(s1==0)
cout<<1<<endl;
else
cout<<s1<<endl;
if(block==1)
cout<<0<<endl;
else
cout<<max(s1,s2)<<endl;
}
int main()
{
// freopen("in.txt","r",stdin);
int n,a,b;
while(~scanf("%d",&n))
{
init();
for(int i=1;i<=n;i++)
{
while(scanf("%d",&a)&&a)
{
edge[s]={a,head[i]};
head[i]=s++;
}
}
for(int i=1;i<=n;i++)
if(!dfn[i])
Tarjan(i,-1);
rebuild(n);
}
return 0;
}