POJ2186_Popular cows_强连通分量_Korasaju算法

来源:互联网 发布:马云在淘宝怎么赚钱 编辑:程序博客网 时间:2024/05/01 04:58

题意:

给一个有向图,找一些点,使得从所有点出发都可以到达这些点

题解:

定理:有向无环图中唯一出度为0的点,一定可以由任何点出发均可达(由于无环,所以从任何点出发往前走,必然终止于一个出度为0的点)

1. 求出所有强连通分量
2. 每个强连通分量缩成一点,则形成一个有向无环图DAG。
3. DAG上面如果有唯一的出度为0的点,则该点能被所有的点可达。那么该点所代表的连通分量上的所有的原图中的点,都能被原图中的所有点可达,则该连通分量的点数,就是答案。
4. DAG上面如果有不止一个出度为0的点,则这些点互相不可达,原问题无解,答案为0

PS:缩点的时候不一定要构造新图,只要把不同强连通分量的点染不同颜色,然后考察各种颜色的点有没有连到别的颜色的边即可(即其对应的缩点后的DAG图上的点是否有出边)。


Korasaju算法求强连通分量:

1.深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。
2.深度优先遍历G的转置图GT,选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历,一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。
3.第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量


图解:

(a)为有向图G,其中的阴影部分是G的强连通分支,

对每个顶点都标出了其发现时刻与完成时刻,黑色边为深度优先搜索的树枝;

(b)G的转置图GT依次以b,c,g,h为起点做DFS, 得到4个强连通分量

原题:

Popular Cows
Time Limit: 2000MS
Memory Limit: 65536KTotal Submissions: 19650
Accepted: 7962

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 31 22 12 3

Sample Output

1


代码:

Run IDUserProblemResultMemoryTimeLanguageCode LengthSubmit Time11819684chengtbf2186Accepted1876K375MSC++2477B2013-07-20 10:35:33


#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#define N 10005#define M 50005using namespace std;typedef struct MyStruct{int u,v;}EDGE;EDGE edge[M];int n,m;int visited_first[N];//记录第一次搜索过程中是否被访问过int visited_second[N];//记录第二次搜索过程中是否被访问过vector<int>G[N];//存图vector<int>G_t[N];//存转置图int dfs_time;int end_time[N];//这里非常巧妙!!!end_time[i]不是代表第i个结点的结束时间,而是结束时间为i的结点序号//!这样就直接省去了一次复杂的排序!而且结束时间与结点标号一一对应!int color[N];//用不同的颜色标记不同的强连通分量int dfs_color;//记录颜色的变量int num_of_color[N];//记录强连通分量内的结点个数int degree[N];//记录不同强连通分量形成的缩点的出度void dfs_first(int u)//第一次深搜搜原图记录结束时间{visited_first[u]=1;int child;for (int i = 0; i <G[u].size(); i++){child=G[u][i];if (!visited_first[child]){dfs_first(child);}}end_time[dfs_time++]=u;}void dfs_second(int u)//第二次深搜搜转置图求强联通分量{visited_second[u]=1;color[u]=dfs_color;//标记当前结点的颜色num_of_color[dfs_color]++;int child;for (int i = 0; i < G_t[u].size(); i++){child=G_t[u][i];if (!visited_second[child]){dfs_second(child);}}}int main(){int i,j,a,b,u,flag,ans;while (scanf("%d%d",&n,&m)!=EOF){//这应该是我做的最坑爹的一次初始化了- -这么多数组for ( i = 0; i <=n ; i++){G[i].clear();G_t[i].clear();}memset(visited_first,0,sizeof(visited_first));memset(visited_second,0,sizeof(visited_second));memset(end_time,0,sizeof(end_time));memset(color,0,sizeof(color));memset(num_of_color,0,sizeof(num_of_color));memset(degree,0,sizeof(degree));dfs_color=1;dfs_time=1;flag=0;//flag变量标记是否有多个出度为0的强连通分量ans=0;for ( i = 1; i <=m ; i++){scanf("%d%d",&a,&b);G[a].push_back(b);//正向存图,a指向bG_t[b].push_back(a);//反向存,存转置图edge[i].u=a;edge[i].v=b;}for ( i = 1; i <=n ; i++){if (!visited_first[i]){dfs_first(i);}}for ( i =n ; i > 0 ; i--){u=end_time[i];if (!visited_second[u])//此标记表示 结束时间为i的结点(是u)是否被访问过{dfs_second(u);dfs_color++;}}for (i = 1; i <=m ; i++){if (  color[ edge[i].u ]  !=  color[ edge[i].v ])//如果当前边的两个结点的颜色不一样,说明分别属于不同的强连通分量{degree[ color[ edge[i].u ] ]++;//则结点u的颜色所代表的强连通分量的出度加一}}for ( i = 1; i <dfs_color ; i++){if (degree[i]==0){ans=num_of_color[i];flag++;}}if (flag>=2){printf("0\n");}else{printf("%d\n",ans);}}return 0;}


原创粉丝点击