poj2186-Popular CowsTarjan+(染色+缩点)(经典)
Posted 00isok
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了poj2186-Popular CowsTarjan+(染色+缩点)(经典)相关的知识,希望对你有一定的参考价值。
<题目链接>
题目大意:
有N(N<=10000)头牛,每头牛都想成为most poluler的牛,给出M(M<=50000)个关系,如(1,2)代表1欢迎2,关系可以传递,但是不可以相互,即1欢迎2不代表2欢迎1,但是如果2也欢迎3那么1也欢迎3.
给出N,M和M个欢迎关系,求被所有牛都欢迎的牛的数量。
解题分析:
仔细思考后发现,其实题目就是问你,在给出的这个有向图中的所有连通分量中,是否存在唯一出度为0的连通分量,如果存在,则输出这个连通分量中所有点的个数。
//dfn[v]: 顶点v被访问的时间; //low[v]: low数组的作用是让某个连通分量的每一个点的low值等于这个连通分量被访问时间最早的那个点,即确定连通分量中的根节点 //tp: 访问时间; //Stack[] : 数组模拟栈,p: 栈的大小; //vis[]: 顶点是否在栈中 //in[]: 缩点后的数组,cnt: 缩点后图中点的个数 //kt[]: 出度数组 #include <cstdio> #include <stack> #include <iostream> #include <string.h> #include <algorithm> using namespace std; typedef long long LL; const int N=1e4+10; const int M=5e4+10; struct Node{ int to; int next; }; Node edge[M]; //链式前向星 int tol,head[M]; int dfn[N],low[N],tp; int Stack[N],p; bool vis[N]; int in[N],cnt; int kt[N]; int n,m; void add(int u,int v) //存图 { edge[tol].to=v; edge[tol].next=head[u]; head[u]=tol++; } void init() { //初始化链式前向星 tol=0; memset(head,-1,sizeof(head)); memset(vis,0,sizeof(vis)); //初始化顶点都不在栈里面 memset(dfn,0,sizeof(dfn)); //初始化访问时间为0 tp=p=0; //初始化时间和栈大小都为0 cnt=0; //缩点初始化,可以理解有cnt种点集 } void Tarjan(int u) { int v; dfn[u]=low[u]=++tp; //初始化dfn=low=访问时间 Stack[++p]=u; //节点入栈 vis[u]=true; //入栈标记 for(int i=head[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(!dfn[v]) //如果还未被访问 { Tarjan(v); low[u]=min(low[u],low[v]); } else if(vis[v]) //在栈里; low[u]=min(low[u],dfn[v]); } int temp; if(dfn[u]==low[u]) //如果是这个强连通分量的根; { cnt++; //缩点标记,给每个连通块染色,打上标记序号 while(1) { temp=Stack[p]; //取元素 vis[temp]=false; //出栈标记 in[temp]=cnt; //缩点,给该连通块中的所有点染色 --p; if(temp==u) //如果与u相等,退出 break; } } } void Solve() { int u,i,v; memset(kt,0,sizeof(kt)); //初始化出度数组 for(u=1;u<=n;u++) { for(i=head[u];i!=-1;i=edge[i].next) { v=edge[i].to; if(in[u]!=in[v]) //如果不属于同一个集合 kt[in[u]]++; //这个集合的出度++ } } int sum=0; //出度个数 int k; //标记是哪个点集 int ans=0; //答案 for(int i=1;i<=cnt;i++) { if(!kt[i]) { sum++; //统计出度为0的连通块的数目 k=i; //标记这个联通块 } } if(sum==1) //如果只有一个连通块出度为0,则符合条件 { for(int i=1;i<=n;i++) //找到这个联通块中点的个数 { if(in[i]==k) //标记为k的连通块中的点 ans++; } printf("%d ",ans); } else puts("0"); } int main() { int u,v; while(~scanf("%d%d",&n,&m)) { init(); while(m--) { scanf("%d%d",&u,&v); add(u,v); } //Tarjan() for(int i=1;i<=n;i++) { if(!dfn[i]) //如果还没有被访问过 Tarjan(i); } Solve(); } return 0; }
2018-08-16
以上是关于poj2186-Popular CowsTarjan+(染色+缩点)(经典)的主要内容,如果未能解决你的问题,请参考以下文章
Poj 2186 Popular Cows(Tarjan 强连通缩点)
POJ 2186 Popular Cows(Targin缩点)