Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 28379 | Accepted: 11488 |
Description
Input
Output
Sample Input
3 3 1 2 2 1 2 3
Sample Output
1
题意:A认为B受欢迎,B认为C受欢迎,那么A就认为C受欢迎。给定N头牛,M个认为受欢迎情况。确定受所有牛欢迎的这些牛的数目。
思路:首先判断有向图是否连通,若不连通则答案为0。若连通则进行缩点(注意有向图与无向图的缩点的不同之处),缩点之后得到一个有向树。若树中存在多个叶子结点,则答案为0,否则答案为 缩成叶子结点的那个连通分量重结点的数目。
#include"cstdio" #include"cstring" #include"vector" using namespace std; const int MAXN=10005; int V,E; vector<int> G[MAXN]; vector<int> rG[MAXN]; int vis[MAXN]; int deg[MAXN]; int pos; void dfs(int u) { vis[u]=1; pos=u; for(int i=0;i<G[u].size();i++) { int to=G[u][i]; if(!vis[to]) { dfs(to); } } } void rdfs(int u) { vis[u]=1; for(int i=0;i<rG[u].size();i++) { int to=rG[u][i]; if(!vis[to]) { rdfs(to); } } } bool pass() { memset(vis,0,sizeof(vis)); dfs(1); memset(vis,0,sizeof(vis)); rdfs(pos); for(int i=1;i<=V;i++) if(!vis[i]) return false; return true; } int index; int dfn[MAXN],low[MAXN]; int stack[MAXN],top; int cpnt[MAXN],cnt; inline int min(int a,int b) { return a > b? b: a; } void tarjan(int u,int fa) { stack[top++]=u; dfn[u]=low[u]=++index; for(int i=0;i<G[u].size();i++) { int to=G[u][i]; if(!dfn[to]) { tarjan(to,u); low[u]=min(low[u],low[to]); } else low[u]=min(low[u],dfn[to]);//注意有向图与无向图缩点的不同 } if(dfn[u]==low[u]) { int v; ++cnt; do{ v=stack[--top]; cpnt[v]=cnt; }while(u!=v); } } int seek() { for(int i=1;i<=V;i++) for(int j=0;j<G[i].size();j++) { int to=G[i][j]; if(cpnt[i]!=cpnt[to]) { deg[cpnt[i]]++; } } int v; int flag=0; for(int i=1;i<=cnt;i++) if(deg[i]==0)//答案肯定为叶子结点 { v=i; flag++; } if(flag>1) return 0;//必须只存在一个叶子结点 int ans=0; for(int i=1;i<=V;i++) if(cpnt[i]==v) ans++; return ans; } int main() { while(scanf("%d%d",&V,&E)!=EOF) { for(int i=1;i<=V;i++) { G[i].clear(); rG[i].clear(); } for(int i=0;i<E;i++) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); rG[v].push_back(u); } if(pass()) { index=0; memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); top=0; cnt=0; memset(cpnt,0,sizeof(cpnt)); tarjan(1,-1); memset(deg,0,sizeof(deg)); int ans=seek(); printf("%d\n",ans); } else { printf("0\n"); } } return 0; }
下面是第二种有向图缩点方法,一次dfs,边反向后再进行一次rdfs.解释见代码
#include"cstdio" #include"cstring" #include"vector" using namespace std; const int MAXN=10005; int V,E; vector<int> G[MAXN]; vector<int> rG[MAXN]; vector<int> vs; bool used[MAXN]; int cmp[MAXN]; void add_edge(int u,int v) { G[u].push_back(v); rG[v].push_back(u); } void dfs(int u) { used[u]=true; for(int i=0;i<G[u].size();i++) if(!used[G[u][i]]) dfs(G[u][i]);//假设u所能访问的结点都处于同一连通分量 vs.push_back(u); //后续遍历 } void rdfs(int u,int k) { used[u]=true; cmp[u]=k; for(int i=0;i<rG[u].size();i++) if(!used[rG[u][i]]) rdfs(rG[u][i],k); } int scc() { memset(used,false,sizeof(used)); for(int i=1;i<=V;i++) if(!used[i]) dfs(i); memset(used,false,sizeof(used)); int k=0; for(int i=vs.size()-1;i>=0;i--)//倒着访问,保证边反向后各个连通分量互不影响 if(!used[vs[i]]) rdfs(vs[i],k++);//对应后续遍历,若边反向后,起点u仍能遍历整个连通分量,那么它们处以同一连通分量中.否则处于不同的连通分量 return k; } int deg[MAXN]; int seek() { memset(deg,0,sizeof(deg)); int k=scc(); for(int i=1;i<=V;i++) for(int j=0;j<G[i].size();j++) { int to=G[i][j]; if(cmp[i]!=cmp[to]) { deg[cmp[i]]++; } } int v=-1; int flag=0; int ans=0; for(int i=1;i<=V;i++) if(deg[cmp[i]]==0) { ans++; if(cmp[i]!=v) { v=cmp[i]; flag++; } if(flag>1) return 0; } return ans; } int main() { while(scanf("%d%d",&V,&E)!=EOF) { for(int i=1;i<=V;i++) { vs.clear(); G[i].clear(); rG[i].clear(); cmp[i]=0; } for(int i=0;i<E;i++) { int u,v; scanf("%d%d",&u,&v); add_edge(u,v); } int ans=seek(); printf("%d\n",ans); } return 0; }
标准tarjan算法对有向图缩点。
#include"cstdio" #include"cstring" #include"vector" using namespace std; const int MAXN=10005; vector<int> G[MAXN]; int V,E; int index; int dfn[MAXN],low[MAXN]; int stack[MAXN],top; int cpnt[MAXN],cnt; inline int min(int a,int b) { return a > b? b: a; } void tarjan(int u) { stack[top++]=u; dfn[u]=low[u]=++index; for(int i=0;i<G[u].size();i++) { int to=G[u][i]; if(!dfn[to]) { tarjan(to); low[u]=min(low[u],low[to]); } else low[u]=min(low[u],dfn[to]); } if(dfn[u]==low[u]) { int v; cnt++; do{ v=stack[--top]; cpnt[v]=cnt; }while(u!=v); } } int deg[MAXN]; bool used[MAXN]; int solve() { for(int i=1;i<=V;i++) if(!dfn[i]) tarjan(i); for(int i=1;i<=V;i++) { for(int j=0;j<G[i].size();j++) { int to=G[i][j]; if(cpnt[i]!=cpnt[to]) { deg[cpnt[i]]++; } } } int flag=0; memset(used,false,sizeof(used)); int ans=0; for(int i=1;i<=V;i++) { if(deg[cpnt[i]]==0) { if(!used[cpnt[i]]) flag++; if(flag>1) return 0; used[cpnt[i]]=true; ans++; } } return ans; } int main() { while(scanf("%d%d",&V,&E)!=EOF) { for(int i=1;i<=V;i++) G[i].clear(); top=0; index=0; cnt=0; memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(cpnt,0,sizeof(cpnt)); memset(deg,0,sizeof(deg)); for(int i=0;i<E;i++) { int u,v; scanf("%d%d",&u,&v); G[u].push_back(v); } int ans=solve(); printf("%d\n",ans); } return 0; }