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
Hint
Cow 3 is the only cow of high popularity.
解题思路:
如果这道题一个个点去求的话,肯定会超时,所以必须采用高效的算法
如果一头牛是最受欢迎的牛的话,那么这个牛所属的强连通分量的所有牛将都是最受欢迎的牛
我们按照双DFS求强连通分量,求得的最后一个连通分量里的牛的数量就是备选解,判断这个连通分量里的任意一个点是否其余点都可达,如果存在点不可达,那么这个连通分量里的牛都不可达,因此就不存在最受欢迎的牛
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <vector>
#include <string.h>
using namespace std;
const int MAXV = 10010; //最大顶点数
vector<int> G[MAXV]; //正序
vector<int> rG[MAXV]; //逆序
vector<int> vs; //后序遍历序列
bool sVisited[MAXV]; //访问
int sCmp[MAXV]; //拓扑序
int N, M;
void add_edge(int from, int to) {
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v) {
sVisited[v] = true;
for (int i = 0; i < G[v].size(); ++i) {
if (!sVisited[G[v][i]]) dfs(G[v][i]);
}
vs.push_back(v); //逆序加入结点
}
void rdfs(int v, int k) {
sVisited[v] = true;
sCmp[v] = k;
for (int i = 0; i < rG[v].size(); ++i) {
if (!sVisited[rG[v][i]]) rdfs(rG[v][i], k);
}
}
int main() {
scanf("%d %d", &N, &M); //读入内容
int a, b;
while (M--) {
scanf("%d %d", &a, &b);
add_edge(a, b);
}
//求解强连通分量
memset(sVisited, false, sizeof(sVisited));
vs.clear();
for (int i = 1; i <= N; ++i) {
if (!sVisited[i]) dfs(i); //深度遍历结点
}
memset(sVisited, false, sizeof(sVisited)); //设置访问
int k = 0; //连通分量个数
for (int i = vs.size() - 1; i >= 0; --i) {
if (!sVisited[vs[i]]) rdfs(vs[i], k++); //求连通块
}
int sans = 0;
int u = 0;
//求解连通分量结点数
for (int i = 1; i <= N; ++i) {
if (sCmp[i] == k-1) { //如果是最后一个连通分量的话
sans++;
u = i; //强连通分量内的点
}
}
//检查是否从所有点可达,就检查逆序后它能否到达其他所有点
memset(sVisited, false, sizeof(sVisited)); //设置访问
rdfs(u, 0);
for (int i = 1; i <= N; ++i) {
if (!sVisited[i]) {
sans = 0;
break;
}
}
printf("%d\n", sans);
system("PAUSE");
return 0;
}