Popular Cows
Time Limit: 2000MS | Memory Limit: 65536K | |
Total Submissions: 30680 | Accepted: 12445 |
Description
Every cow‘s dream is to become the most popular cow in the herd. In a herd of N (1 <= N <= 10,000) cows, you are given up to M (1 <= M <= 50,000) ordered pairs of the form (A, B) that tell you that cow A thinks that cow B is popular. Since popularity is transitive, if A thinks B is popular and B thinks C is popular, then A will also think that C is
popular, even if this is not explicitly specified by an ordered pair in the input. Your task is to compute the number of cows that are considered popular by every other cow.
Input
* Line 1: Two space-separated integers, N and M
* Lines 2..1+M: Two space-separated numbers A and B, meaning that A thinks B is popular.
Output
* Line 1: A single integer that is the number of cows who are considered popular by every other cow.
Sample Input
3 3 1 2 2 1 2 3
Sample Output
1 大意:n只牛,m对数(a,b)表示a认为b牛逼。若a认为b牛逼,b认为c牛逼,那么a也认为c牛逼。问n只牛中,有多少只牛是其他所有牛都认为牛逼的。 分析:若图为不连通,则答案为0.当图连通时,在同一个强连通分量A中的牛在被该强连通分量中其他牛认为牛逼;若在A和另一个强连通分量B之间有路,假设A指向B,则A中的牛都认为B中的牛是牛逼的。任意一个有向图都可以分解成若干不相交的强连通分量,把每个强连通分量缩点,就得到一个DAG(有向无环图)。该题要找的是缩点后DAG中出度为0的点所表示的强连通分量中的点数。(该出度为0的点一定唯一,否则不可能有其他牛都认为牛逼的牛) 求强连通分量可使用tarjan算法。
Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。
定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。
当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。
void tarjan(int s) { vis[s]=2; dfn[s]=low[s]=++now; sta.push(s); for(int i=0; i<gra[s].size(); i++) { int t=gra[s][i]; if(dfn[t]==0) { tarjan(t); low[s]=min(low[s],low[t]); } else if(vis[t]==2) low[s]=min(low[s],dfn[t]); } if(low[s]==dfn[s]) { sum++; while(!sta.empty()) { int t=sta.top(); sta.pop(); vis[t]=1; num[t]=sum; if(t==s) break; } } }
AC代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<stdlib.h> #include<vector> #include<stack> using namespace std; #define LL long long #define N 10005 vector<int> gra[N]; stack<int>sta; int n,m,dfn[N],low[N],vis[N],num[N]; int degree[N],sum,now,res,loc; void init() { sum=0; now=0; res=0; memset(vis,0,sizeof(vis)); memset(low,0,sizeof(low)); memset(dfn,0,sizeof(dfn)); memset(degree,0,sizeof(degree)); for(int i=0; i<=n; i++) gra[i].clear(); while(!sta.empty()) sta.pop(); } void read() { int a,b; scanf("%d%d",&n,&m); for(int i=0; i<m; i++) { scanf("%d%d",&a,&b); gra[a].push_back(b); } } void tarjan(int s) { vis[s]=2; dfn[s]=low[s]=++now; sta.push(s); for(int i=0; i<gra[s].size(); i++) { int t=gra[s][i]; if(dfn[t]==0) { tarjan(t); low[s]=min(low[s],low[t]); } else if(vis[t]==2) low[s]=min(low[s],dfn[t]); } if(low[s]==dfn[s]) { sum++; while(!sta.empty()) { int t=sta.top(); sta.pop(); vis[t]=1; num[t]=sum; //缩点 if(t==s) break; } } } void solve() { for(int i=1; i<=n; i++) if(!dfn[i]) tarjan(i); for(int i=1; i<=n; i++) for(int j=0; j<gra[i].size(); j++) if(num[i]!=num[gra[i][j]]) degree[num[i]]++; //对缩点后的DAG进行出度的统计 for(int i=1; i<=sum; i++) { if(degree[i]==0) { res++; loc=i; //第几个强连通分量 } } if(res>1) printf("0\n"); else { res=0; for(int i=1; i<=n; i++) if(num[i]==loc) res++; printf("%d\n",res); } } int main() { init(); read(); solve(); return 0; } /* 3 3 1 2 2 1 2 3 */