Time Limit: 2000MS | Memory Limit: 65536K | |
Description
Input
Output
Sample Input
3 3 1 2 2 1 2 3
Sample Output
1
题目大意:给定一个有向图,求有多少个顶点是由任何顶点出发都可达的?
初次接触这方面的图论,知道的太少了,还不能看出如何转换,只能根据题解慢慢学习
要做这题首先得了解一个定理:DAG中唯一出度为0的点,一定可以由任何点出发均可达(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)
可以先求出所有的强连通分量,并且将强连通分量缩成一个点,寻找出度为0的缩点,若其个数大于1,则有0个点满足题意;若其个数等于1,则缩点中所有的点都满足题意
#include <cstdio> #include <cstring> #include <vector> using namespace std; int n,m,s,e,cnt; int stak[10005],top,color[10005],indeg[10005],outdeg[10005];//top指向栈顶元素的后一个,也表示栈内元素个数 vector<int> edge[10005],edge_T[10005]; bool vis[10005]; void dfs(int u) { vis[u]=true; for(int i=0;i<edge[u].size();++i) if(!vis[edge[u][i]]) dfs(edge[u][i]); stak[top++]=u;//点u离开时入栈 } void dfs_T(int u) { vis[u]=true; color[u]=cnt;//对u点进行染色,即缩点 for(int i=0;i<edge_T[u].size();++i) if(!vis[edge_T[u][i]]) dfs_T(edge_T[u][i]); } void Kosaraju() {//求出所有强连通分量 cnt=top=0; memset(vis,false,sizeof(vis)); for(int i=1;i<=n;++i) if(!vis[i]) dfs(i); memset(vis,false,sizeof(vis)); while(top>0) { s=stak[--top]; if(!vis[s]) { ++cnt; dfs_T(s); } } } int solve() { memset(indeg,0,sizeof(indeg)); memset(outdeg,0,sizeof(outdeg)); for(int i=1;i<=n;++i) { for(int j=0;j<edge[i].size();++j) { if(color[i]!=color[edge[i][j]]) {//如果这两个点不再一个强连通分量(缩点)上 ++outdeg[color[i]];//点i所在的缩点出度+1 ++indeg[color[edge[i][j]]];//点edge[i][j]所在的缩点入度+1 } } } int num=0;//num表示出度为0的缩点的个数 for(int i=1;i<=cnt;++i) { if(outdeg[i]==0) { ++num; e=i;//e表示出度为0的缩点 } } if(num>1) return 0; int x=0;//x表示缩点e所含的点的个数 for(int i=1;i<=n;++i) if(color[i]==e) ++x; return x; } int main() { while(scanf("%d%d",&n,&m)==2) { for(int i=1;i<=n;++i) { edge[i].clear(); edge_T[i].clear(); } while(m-->0) { scanf("%d%d",&s,&e); edge[s].push_back(e); edge_T[e].push_back(s); } Kosaraju(); printf("%d\n",solve()); } return 0; }