强连通分量


1.缩点成一个有向无环图


2.如果有没被遍历到的点就要输出-1


3.统计每个点的入度


4.入度为0的点的数量就是答案


50分代码

#include<bits/stdc++.h>
using namespace std;
int n,m,num,k,cnt,ans;
int in[505],x[505],a[505],head[505];
int dfn[505],low[505],vis[505],tem[505];
stack<int>s;
struct Node{
	int to,next;
}edge[505*505];
void add(int u,int v){
	edge[++num].to=v;
	edge[num].next=head[u];
	head[u]=num;
}
void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	s.push(x);
	vis[x]=1;
	for(int i=head[x];i;i=edge[i].next){
		int y=edge[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(vis[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(low[x]==dfn[x]){
		k++;
		while(s.top()!=x){
			int q=s.top();
			vis[q]=0;
			tem[q]=cnt;
			s.pop();
		}
		tem[x]=cnt;
		s.pop();
		vis[x]=0;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>x[i]; 
	}
	for(int i=1;i<=n;i++){
		cin>>a[i];
		for(int j=1;j<=a[i];j++){
			cin>>k;
			add(i,k);
		}
	}
	for(int i=1;i<=m;i++){
		if(!dfn[x[i]]){
			tarjan(x[i]);
		}
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			cout<<"-1";
			return 0;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=edge[j].next){
			int v=edge[j].to;
			if(tem[i]!=tem[v]){
				in[v]++;
			}
		}
	}
	for(int i=1;i<=m;i++){
		if(!in[x[i]]){
			ans++;
		} 
	}
	cout<<ans;
	return 0;
}


100分代码

#include<bits/stdc++.h>
using namespace std;
int n,m,num,k,cnt,ans;
int in[505],x[505],a[505],head[505];
int dfn[505],low[505],vis[505],tem[505];
stack<int>s;
vector<int>rul;
struct Node{
	int to,next;
}edge[505*505];
void add(int u,int v){
	edge[++num].to=v;
	edge[num].next=head[u];
	head[u]=num;
}
void tarjan(int x){
	dfn[x]=low[x]=++cnt;
	s.push(x);
	vis[x]=1;
	for(int i=head[x];i;i=edge[i].next){
		int y=edge[i].to;
		if(!dfn[y]){
			tarjan(y);
			low[x]=min(low[x],low[y]);
		}
		else if(vis[y]){
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(low[x]==dfn[x]){
		k++;
		while(s.top()!=x){
			int q=s.top();
			vis[q]=0;
			tem[q]=cnt;
			s.pop();
		}
		tem[x]=cnt;
		s.pop();
		vis[x]=0;
	}
}
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>x[i]; 
	}
	for(int i=1;i<=n;i++){
		cin>>a[i];
		for(int j=1;j<=a[i];j++){
			cin>>k;
			add(i,k);
		}
	}
	for(int i=1;i<=m;i++){
		if(!dfn[x[i]]){
			rul.push_back(x[i]);
			tarjan(x[i]);
		}
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			cout<<"-1";
			return 0;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=head[i];j;j=edge[j].next){
			int v=edge[j].to;
			if(tem[i]!=tem[v]){
				in[v]++;
			}
		}
	}
	for(int i=0;i<rul.size();i++){
		if(!in[rul[i]]){
			ans++;
		} 
	}
	cout<<ans;
	return 0;
}

我们发现区别在于100分的代码,在最后没有判断能从一个点到达的点

有点绕,没关系,我们看个图

图中4号点为1,2,3号点的缩点

你会发现老大连的1号点入度为0,但是在新图中的入度却不是0

这就会导致答案错误

其实你也可以通过统计新图的入度来避免这种错误

end