题目链接<http://poj.org/problem?id=1236>
题意:
n个学校,学校之间被单向边连接。有两个问题:
1、要给几个学校软件,才能让所有学校都能收到。
2、至少要加几条边,才能使原图变成强连通图。
题目保证给出的图是一个连通图。
题解:
Tarjan缩点,重新构图。
入度为零的个数就是第一问的答案。
max(入度为零,出度为零)是第二问的答案。
另外注意,如果本身就是强连通图,那么答案就是1,0了,这个要特判一下。
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<algorithm>
#include<stack>
using namespace std;
const int N=105;
int n;
struct Edge{
int u,v,w,nxt;
Edge(int u=0,int v=0,int w=0,int nxt=0):u(u),v(v),w(w),nxt(nxt){}
}edge[N*N],e[N*N];
int p[N],edn;
void add(int u,int v,int w){
edge[++edn]=Edge(u,v,w,p[u]);p[u]=edn;
}
int hd[N],en;
void ad(int u,int v,int w){
e[++en]=Edge(u,v,w,hd[u]);hd[u]=en;
}
int dfn[N],low[N],st[N],scc[N],index,top,sccnum;
void tarjan(int u){
dfn[u]=low[u]=++index;
st[++top]=u;
for(int i=p[u];~i;i=edge[i].nxt){
int v=edge[i].v;
if(!dfn[v]){
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(!scc[v]) low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
sccnum++;
while(1){
int x=st[top--];
scc[x]=sccnum;
if(x==u) break;
}
}
}
int in[N],out[N];
void rebuild(){
for(int x=1;x<=n;x++){
int u=scc[x];
for(int i=p[x];~i;i=edge[i].nxt){
int y=edge[i].v;
int v=scc[y];
if(u==v) continue;
ad(u,v,1);
in[v]++;out[u]++;
}
}
}
int main(){
int x;
scanf("%d",&n);
memset(p,-1,sizeof(p));edn=-1;
memset(hd,-1,sizeof(hd));en=-1;
for(int i=1;i<=n;i++){
while(scanf("%d",&x)){
if(x==0) break;
add(i,x,1);
}
}
for(int i=1;i<=n;i++){
if(!scc[i]) tarjan(i);
}
rebuild();
int iz=0,oz=0;
for(int i=1;i<=sccnum;i++){
if(in[i]==0) iz++;
if(out[i]==0) oz++;
}
if(sccnum==1) printf("1\n0\n");
else printf("%d\n%d\n",iz,max(iz,oz));
}