思路:首先用tarjan算法算出每个点属于哪个连通块,再缩点后求出各个连通块的入度出度。
入度为0的点的个数就是问一的解。
入度和出度为0的点个数多的就是问二的解。
代码:
#include<stdio.h>
#include<string.h>
#include<vector>
#include<stack>
using namespace std;
const int maxn = 119;
int n , in[maxn] , out[maxn];
vector<int> G[maxn];
int pre[maxn] , lowlink[maxn] , sccno[maxn] , dfs_clock , scccnt;
stack<int> S;
void dfs(int u){
pre[u] = lowlink[u] = ++dfs_clock;
S.push(u);
for(int i = 0 ; i < G[u].size() ; i ++){
int v = G[u][i];
if(!pre[v]){
dfs(v);
lowlink[u] = min(lowlink[v] , lowlink[u]);
}
else if(!sccno[v]){
lowlink[u] = min(lowlink[u] , pre[v]);
}
}
if(lowlink[u] == pre[u]){
scccnt ++;
int x;
do{
x = S.top();
S.pop();
sccno[x] = scccnt;
}while(x != u);
}
}
void findscc(int n){
dfs_clock = scccnt = 0;
memset(pre , 0 , sizeof(pre));
memset(sccno , 0 , sizeof(sccno));
memset(lowlink , 0 , sizeof(lowlink));
for(int i = 0 ; i < n ; i ++){
if(!pre[i]) dfs(i);
}
return ;
}
int main(){
while(~scanf("%d" , &n)){
int t;
for(int i = 0 ; i < n ; i ++){
G[i].clear();
while(~scanf("%d" , &t)){
if(t == 0) break;
else{
t --;
G[i].push_back(t);
}
}
}
findscc(n);
memset(in , 0 , sizeof(in));
memset(out , 0 , sizeof(out));
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < G[i].size() ; j ++){
int v = G[i][j];
if(sccno[i] != sccno[v]) {
out[sccno[i]] ++;
in[sccno[v]] ++;
}
}
}
int a = 0 , b = 0;
for(int i = 1 ; i <= scccnt ; i ++){
if(in[i] == 0) a ++;
if(out[i] == 0) b ++;
}
int c = max(a , b);
if(scccnt == 1){
c = 0;
}
printf("%d\n%d\n" , a , c);
}
return 0;
}