Popular Cows
Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 23445   Accepted: 9605

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

Hint

Cow 3 is the only cow of high popularity. 

Source

题意:能够转换成“给定一些有向路,求有多少个点能够由其余的随意点到达。

题解:第一道强连通分量的题,大致总结下Kosaraju算法:求强连通分量主要是为了简化图的构造,假设分量外的一个点能到达分量内的当中一个点,那么它必然能到达分量内的全部点,所以某种程度上。强连通分量能够简化成一个点。详细的求解过程是:1、随意选定一个点開始对原图进行深搜,记录每一个点离开时的时间(更确切的说是求每一个时间相应哪个点离开)。2、对原图的反图进行深搜,步骤一中最后离开的点最先開始深搜。每次将同一棵树中的点都哈希成同一个值。最后有多少棵树就有多少个强连通分量。

这题最后全部点都哈希完毕后实际上构成了一个DAG。假设新图中出度为0的点仅仅有一个那么有解,解为该出度为0的强连通分量中原来点的个数。若出度为0的点不止一个,那么无解,由于有两群牛互不崇拜,此时答案为0.在推断连通分量是否有出度时有个小技巧,就是在对反图DFS时若发现连接到的点已訪问且它的哈希值与当前訪问点的哈希值不同。那么这个被连接到的点相应的联通分量是有出度的。然后还需记录每一个连通分量的点数。

#include <stdio.h>
#include <string.h>
#define maxn 10002
#define maxm 50002 int head0[maxn], head1[maxn], id;
int count[maxn], num[maxn], hash[maxn];
struct Node{
int t0, next0, t1, next1;
} E[maxm];
bool vis[maxn], out[maxn]; void addEdge(int u, int v)
{
E[id].t0 = v; E[id].next0 = head0[u];
head0[u] = id; E[id].t1 = u;
E[id].next1 = head1[v]; head1[v] = id++;
} void getMap(int n, int m)
{
int i, u, v; id = 0;
memset(head0, -1, sizeof(int) * (n + 1)); //save time
memset(head1, -1, sizeof(int) * (n + 1));
for(i = 0; i < m; ++i){
scanf("%d%d", &u, &v);
addEdge(u, v);
}
} void DFS0(int pos, int& sig)
{
vis[pos] = 1; int i;
for(i = head0[pos]; i != -1; i = E[i].next0){
if(!vis[E[i].t0]) DFS0(E[i].t0, sig);
}
num[++sig] = pos;
} void DFS1(int pos, int sig)
{
vis[pos] = 1; hash[pos] = sig;
int i; ++count[sig];
for(i = head1[pos]; i != -1; i = E[i].next1){
if(!vis[E[i].t1]) DFS1(E[i].t1, sig);
else if(hash[E[i].t1] != hash[pos]) out[hash[E[i].t1]] = 1;
}
} void solve(int n) //Kosaraju
{
int i, sig = 0, tmp = 0, ans;
memset(vis, 0, sizeof(bool) * (n + 1));
for(i = 1; i <= n; ++i)
if(!vis[i]) DFS0(i, sig);
memset(vis, 0, sizeof(bool) * (n + 1));
memset(count, 0, sizeof(int) * (n + 1));
memset(out, 0, sizeof(bool) * (n + 1));
i = sig; sig = 0;
for(; i; --i)
if(!vis[num[i]]) DFS1(num[i], ++sig);
for(i = 1; i <= sig; ++i)
if(!out[i]) ++tmp, ans = count[i];
//printf("sig%d\n", sig);
if(tmp == 1) printf("%d\n", ans);
else printf("0\n");
} int main()
{
int n, m;
while(scanf("%d%d", &n, &m) == 2){
getMap(n, m);
solve(n);
}
return 0;
}

Tarjan解法:

#include <stdio.h>
#include <string.h>
#define maxn 10002
#define maxm 50002 int head[maxn], vis[maxn], id, id2, scc_num, sec;
int dfn[maxn], low[maxn], sta[maxn], count[maxn];
bool out[maxn];
struct Node{
int to, next;
} E[maxm]; int min(int a, int b){
return a < b ? a : b;
} void addEdge(int u, int v)
{
E[id].to = v;
E[id].next = head[u];
head[u] = id++;
} void getMap(int n, int m)
{
int i, u, v; id = 0;
memset(head, -1, sizeof(int) * (n + 1));
memset(vis, 0, sizeof(int) * (n + 1));
memset(out, 0, sizeof(bool) * (n + 1));
memset(count, 0, sizeof(int) * (n + 1));
for(i = 0; i < m; ++i){
scanf("%d%d", &u, &v);
addEdge(u, v);
}
} void DFS(int pos) //强连通分量必然是该树的子树
{
dfn[pos] = low[pos] = ++sec;
vis[pos] = 1; sta[id2++] = pos;
int i, u, v;
for(i = head[pos]; i != -1; i = E[i].next){
v = E[i].to;
if(!vis[v]) DFS(v);
if(vis[v] == 1)
low[pos] = min(low[pos], low[v]);
}
if(dfn[pos] == low[pos]){
++scc_num;
do{
++count[scc_num];
u = sta[--id2];
low[u] = scc_num;
vis[u] = 2;
} while(u != pos);
}
} void solve(int n) //Tarjan
{
int i, j, ok = 0, ans; sec = id2 = scc_num = 0;
for(i = 1; i <= n; ++i)
if(!vis[i]) DFS(i);
for(i = 1; i <= n; ++i)
for(j = head[i]; j != -1; j = E[j].next)
if(low[i] != low[E[j].to]){
out[low[i]] = 1; break;
}
for(i = 1; i <= scc_num; ++i)
if(!out[i]){
if(++ok > 1) break;
ans = count[i];
}
if(ok != 1) printf("0\n");
else printf("%d\n", ans);
} int main()
{
int n, m;
while(scanf("%d%d", &n, &m) == 2){
getMap(n, m);
solve(n);
}
return 0;
}

Garbow解法:与Tarjan思想同样,仅仅是实现方式略有不同,效率更高一些。

#include <stdio.h>
#include <string.h>
#define maxn 10002
#define maxm 50002
//sta2用以维护当前连通分量的根
int head[maxn], id, sta1[maxn], id1, sta2[maxn], id2;
int low[maxn], scc[maxn], sccNum, sec, count[maxn];
struct Node{
int to, next;
} E[maxm];
bool out[maxn]; void addEdge(int u, int v)
{
E[id].to = v;
E[id].next = head[u];
head[u] = id++;
} void getMap(int n, int m)
{
int i, u, v; id = 0;
memset(head, -1, sizeof(int) * (n + 1));
for(i = 0; i < m; ++i){
scanf("%d%d", &u, &v);
addEdge(u, v);
}
} void Garbow(int pos)
{
low[pos] = ++sec;
sta1[id1++] = sta2[id2++] = pos;
for(int i = head[pos]; i != -1; i = E[i].next){
if(!low[E[i].to]) Garbow(E[i].to);
else if(!scc[E[i].to]){
while(low[sta2[id2-1]] > low[E[i].to]) --id2;
}
}
if(pos == sta2[id2-1]){
int v; ++sccNum; --id2;
do{
v = sta1[--id1];
scc[v] = sccNum;
++count[sccNum];
} while(sta1[id1] != pos);
}
} void solve(int n)
{
int i, j; id1 = id2 = sec = sccNum = 0;
memset(low, 0, sizeof(int) * (n + 1));
memset(scc, 0, sizeof(int) * (n + 1));
memset(count, 0, sizeof(int) * (n + 1));
memset(out, 0, sizeof(bool) * (n + 1));
for(i = 1; i <= n; ++i)
if(!low[i]) Garbow(i);
for(i = 1; i <= n; ++i)
for(j = head[i]; j != -1; j = E[j].next)
if(scc[i] != scc[E[j].to]){
out[scc[i]] = 1; break;
}
int tmp = 0, ans;
for(i = 1; i <= sccNum; ++i)
if(!out[i]){
if(++tmp > 1){
ans = 0; break;
}
ans = count[i];
}
printf("%d\n", ans);
} int main()
{
int n, m;
while(scanf("%d%d", &n, &m) == 2){
getMap(n, m);
solve(n);
}
return 0;
}

POJ2186 Popular Cows 【强连通分量】+【Kosaraju】+【Tarjan】+【Garbow】的更多相关文章

  1. POJ2186 Popular Cows 强连通分量tarjan

    做这题主要是为了学习一下tarjan的强连通分量,因为包括桥,双连通分量,强连通分量很多的求法其实都可以源于tarjan的这种方法,通过一个low,pre数组求出来. 题意:给你许多的A->B ...

  2. POJ2186 Popular Cows [强连通分量|缩点]

    Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 31241   Accepted: 12691 De ...

  3. poj 2186 Popular Cows (强连通分量+缩点)

    http://poj.org/problem?id=2186 Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissi ...

  4. POJ 2186 Popular Cows --强连通分量

    题意:给定一个有向图,问有多少个点由任意顶点出发都能达到. 分析:首先,在一个有向无环图中,能被所有点达到点,出度一定是0. 先求出所有的强连通分支,然后把每个强连通分支收缩成一个点,重新建图,这样, ...

  5. poj2186 Popular Cows --- 强连通

    给一个有向图,问有多少结点是其它全部结点都能够到达的. 等价于,在一个有向无环图上,找出度为0 的结点.假设出度为0的结点仅仅有一个,那么这个就是答案.假设大于1个.则答案是0. 这题有环.所以先缩点 ...

  6. POJ 2186 Popular Cows 强连通分量模板

    题意 强连通分量,找独立的块 强连通分量裸题 #include <cstdio> #include <cstdlib> #include <cstring> #in ...

  7. POJ 2186 Popular Cows(强连通分量缩点)

    题目链接:http://poj.org/problem?id=2186 题目意思大概是:给定N(N<=10000)个点和M(M<=50000)条有向边,求有多少个“受欢迎的点”.所谓的“受 ...

  8. 强连通分量tarjan缩点——POJ2186 Popular Cows

    这里的Tarjan是基于DFS,用于求有向图的强联通分量. 运用了一个点dfn时间戳和low的关系巧妙地判断出一个强联通分量,从而实现一次DFS即可求出所有的强联通分量. §有向图中, u可达v不一定 ...

  9. 强连通分量的模版 Kosaraju+Tarjan+Garbow

    PS:在贴出代码之前,我得说明内容来源——哈尔滨工业大学出版的<图论及应用>.虽然有一些错误的地方,但是不得不说是初学者该用的书. 从效率的角度来说,Kosaraju <Tarjan ...

  10. 有向图强连通分量的Tarjan算法和Kosaraju算法

    [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极 ...

随机推荐

  1. DotNet 资源大全(Awesome最新版)

    发表时间:2016-09-20 21:34:58   编辑:机器猫   阅读:136次   目录 API 应用框架(Application Frameworks) 应用模板(Application T ...

  2. 转:Struts2框架安全缺陷

    当前java开发网站,通常不会是纯JSP的,大都使用了java framework. 有了这些framework,让开发人员更加快速的开发出代码,也让代码非常具有可扩展性,那些分层架构的思想,更是深入 ...

  3. 2015第6周三ztree的使用

    今天第一次真正在开发中使用了ztree组件,在实践过程体会了从生到熟的乐趣,从开始看不太懂ztree的api到后面熟悉理解其API布局,学习其中的各demo,感觉很不错,简单记录下使用ztree的经验 ...

  4. (续)线性表之双向链表(C语言实现)

    在前文实现单向链表的基本操作下,本文实现双向链表的基本操作. 双向链表与单链表差异,是双向链表结点中有前向指针和后向指针.所以在插入和删除新结点元素时候不见要考虑后向指针还要考虑前向指针. 以下是双向 ...

  5. 能上QQ无法打开网页

    能上QQ无法上网电脑故障解决方法 Winsock协议配置故障解决方法 第1步 :单击开始菜单中的运行,并在打开的运行窗口中键入“cmd”并回车确定,打死命令提示符窗口. 第2步 :在打开的命令提示符窗 ...

  6. Docker自学资源

    1. Docker 的官方文档和博客: Docker官方文档 Docker Blog(官方博客 ) 2. Docker中文这区 网站上的[Docker手册]以及[Docker ppt]两个栏目有 Do ...

  7. linux通过文件查找依赖关系

    通过文件查找安装包安装缺少libstdc++6这个文件在ls /usr/lib/libstd*下有两个文件/usr/lib/libstdc++.so.6 /usr/lib/libstdc++.so.6 ...

  8. C++ Primer 读书笔记:第11章 泛型算法

    第11章 泛型算法 1.概述 泛型算法依赖于迭代器,而不是依赖容器,需要指定作用的区间,即[开始,结束),表示的区间,如上所示 此外还需要元素是可比的,如果元素本身是不可比的,那么可以自己定义比较函数 ...

  9. php学习网址

    后面会陆续维护此页. 1. php编程 此博客是网站www.beilei123.cn镜像,转载请注明出处.

  10. 编写jquery插件

    一.类级别($.extend) 类级别你可以理解为拓展jquery类,最明显的例子是$.ajax(...),相当于静态方法. 开发扩展其方法时使用$.extend方法,即jQuery.extend(o ...