题意:
简单来说,给出一张有向图,问至少选择几个点可以遍历全图 和 至少加几条边使任一个点出发都可以遍历全图
分析:
这是一个与强连通分量有关的问题,在强连通分量中任意一个点都可以到达其他点,那么首先对整张图进行缩点操作,将整张图的强联通分量提取出来,tarjan算法操作之后很容易观察到一张图不存在环了,那么此时记录ans1为树上入度为0的点,ans2记录树上出度为0的点,那么问题一即为ans1,问题二即为max(ans1,ans2),注意当整张图就是强联通的时候特判。
#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");
using namespace std;
#define INF 0x3f3f3f3f
#define maxn 200010
int x[maxn],y[maxn];
int fir[maxn],nex[maxn],v[maxn],e_max;
int n,m,dfn[maxn],temp[maxn],low[maxn],st[maxn];
int in[maxn],out[maxn];
void init()
{
memset(fir,-1,sizeof fir);
e_max=0;
}
void add_edge(int s,int t)
{
int e=e_max++;
v[e]=t;
nex[e]=fir[s];
fir[s]=e;
}
int tarbfs(int k,int lay,int &scc_num)
{
temp[k]=1;
low[k]=lay;
dfn[k]=lay;
st[++m]=k;
for(int i=fir[k]; ~i; i=nex[i])
{
int e=v[i];
if(temp[e]==0)
{
tarbfs(e,++lay,scc_num);
}
if(temp[e]==1) low[k]=min(low[k],low[e]);
}
if(dfn[k]==low[k])
{
++scc_num;
do
{
low[st[m]]=scc_num;
temp[st[m]]=2;
}while(st[m--]!=k);
}
return 0;
}
int tarjan(int n)
{
int scc_num=0,lay=1;
m=0;
memset(temp,0,sizeof temp);
memset(low,0,sizeof low);
for(int i=1; i<=n; i++)
{
if(temp[i]==0) tarbfs(i,lay,scc_num);
}
return scc_num;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
init();
int cnt=0;
for(int i=1; i<=n; i++)
{
while(1)
{
int a;
scanf("%d",&a);
if(!a) break;
x[cnt]=i;
y[cnt++]=a;
add_edge(i,a);
}
}
int lim=tarjan(n);
for(int i=1; i<=n; i++) in[i]=out[i]=0;
for(int i=0; i<cnt; i++)
{
int _x=low[x[i]],_y=low[y[i]];
if(_x!=_y)
{
out[_x]++;
in[_y]++;
}
}
int ans1=0,ans2=0;
for(int i=1; i<=lim; i++)
{
if(in[i]==0) ans1++;
else if(out[i]==0) ans2++;
}
if(lim==1) printf("1\n0\n");
else printf("%d\n%d\n",ans1,max(ans1,ans2));
}
return 0;
}