poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)

/*
   题目大意:有N个cows, M个关系
   a->b 表示 a认为b  popular;如果还有b->c, 那么就会有a->c
   问最终有多少个cows被其他所有cows认为是popular!

   思路:强连通分量中每两个节点都是可达的! 通过分解得到最后一个连通分量A,
   如果将所有的强连通分量看成一个大的节点,那么A一定是孩子节点(因为我们先
   完成的是父亲节点的强连通分量)! 最后如果其他的强连通分量都可以指向A,那么
   A中的每一个cow都会被其他cows所有的cows认为popular!
*/
#include <string>
#include <cstdio>
#include <cstring>
#include <iostream>
#include<vector>
#define M 10005
using namespace std;

vector<int>ex[M];
vector<int>ey[M];

int n, m;
int cnt[M];//记录第一次dfs的节点的逆序
int vis[M];//标记节点是否已经被访问过了
int mark[M];//标记每一个节点是属于哪一个连通分量
int ans;
int top;

void dfs1(int u){//出度遍历
    if(!vis[u]){
       vis[u]=1;
       int len=ex[u].size();
       for(int i=0; i<len; ++i){
           int v=ex[u][i];
           dfs1(v);
       }
       cnt[top++]=u;
    }
}

void dfs2(int u){//入度遍历
   if(!vis[u]){
      vis[u]=1;
      mark[u]=ans;
      int len=ey[u].size();
      for(int i=0; i<len; ++i){
         int v=ey[u][i];
         dfs2(v);
      }
   }
}

int main(){
   while(scanf("%d%d", &n, &m)!=EOF){
      while(m--){
         int u, v;
         scanf("%d%d", &u, &v);
         ex[u].push_back(v);
         ey[v].push_back(u);
      }
      ans=top=0;
      for(int i=1; i<=n; ++i)
         if(!vis[i])
             dfs1(i);

      memset(vis, 0, sizeof(vis));

      for(int i=top-1; i>=0;  --i)
          if(!vis[cnt[i]]){
             ++ans;
             dfs2(cnt[i]);
          }
      int count=0;
      int u=0;
      for(int i=1; i<=n; ++i)
           if(mark[i]==ans){
              ++count;
              u=i;
           }
      memset(vis, 0, sizeof(vis));
      dfs2(u);

      for(int i=1; i<=n; ++i)//其他的强连通分量是否都指向了最后一个强连通分量
        if(!vis[i]){
           count=0;
           break;
        }
      printf("%d\n", count);
      for(int i=1; i<=n; ++i){
         ex[i].clear();
         ey[i].clear();
      }
      memset(vis, 0, sizeof(vis));
   }
   return 0;
}
/*
tarjan 算法果然nb! 首先我们利用该算法将所有的强连通分量分开!
然后将每一个连通分量看成是一个点,这样就成了一个有向无环图!
接着判断初度为 0 的点一共有多少个!如果只有一个,那么最终的答案就是
这个节点终所有子节点的个数!也就是说这个节点中的每一个子节点都能
其他的所有节点到达!

如果初度为 0 的点多余1个,那么对不起,不能满足某个节点恰好能被其他所有
的节点访问到!
*/#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<cstring>
#define M 10005
using namespace std;

vector<int>edge[M];
stack<int>s;
int low[M], vis[M];
int sccN[M], pre[M];
int n, m;
int dfs_clock, cnt;

void dfs(int u){//tarjan 算法
   int len = edge[u].size();
   pre[u]=low[u]=++dfs_clock;
   s.push(u);
   for(int i=0; i<len; ++i){
       int v=edge[u][i];
       if(!pre[v]){
          dfs(v);
          low[u]=min(low[u], low[v]);
       }
       else if(!sccN[v])
          low[u] = min(low[u], pre[v]);
   }
   if(low[u]==pre[u]){
       ++cnt;
       while(1){
         int v=s.top();
         s.pop();
         sccN[v]=cnt;
         if(u==v) break;
       }
   }
}

int main(){
   while(scanf("%d%d", &n, &m)!=EOF){
       dfs_clock=cnt=0;
       memset(pre, 0, sizeof(pre));
       memset(sccN, 0, sizeof(sccN));
       memset(vis, 0, sizeof(vis));
       while(m--){
          int u, v;
          scanf("%d%d", &u, &v);
          edge[u].push_back(v);
       }
       for(int i=1; i<=n; ++i)
          if(!pre[i])
             dfs(i);
       int num=0;
       for(int i=1; i<=n; ++i)
          if(sccN[i]==1)
             ++num;
       int count=0;
       memset(vis, 0, sizeof(vis));
       for(int i=1; i<=n; ++i){
           int len=edge[i].size();
           for(int j=0; j<len; ++j)
              if(sccN[i] != sccN[edge[i][j]]){
                 vis[sccN[i]]=1;
                 break;
              }
       }

       for(int i=1; i<=cnt; ++i)
         if(!vis[i]) ++count;
       if(count==1)
          printf("%d\n", num);
       else printf("0\n");
       for(int i=1; i<=n; ++i)
          edge[i].clear();
       while(!s.empty())
          s.pop();
   }
   return 0;
}
/*比较慢的方法就是:利用tarjan算法将所有的强连通分量进行分离之后,
 将每一个强连通分量看成是一个点,如果有满足我们答案的解,那么初度为零
 点一定只有一个,并且这个点的所有子节点的编号是 1!那么我们先计算出子节点
 编号为 1的个数, 然后在判断其他的强连通分量的节点是否能够到达编号为 1 的
 强连通分量! */
#include<iostream>
#include<cstdio>
#include<vector>
#include<stack>
#include<cstring>
#define M 10005
using namespace std;

vector<int>edge[M];
stack<int>s;
int low[M], vis[M], used[M];
int sccN[M], pre[M];
int n, m;
int dfs_clock, cnt, sum, xx;

void dfs(int u){
   int len = edge[u].size();
   pre[u]=low[u]=++dfs_clock;
   s.push(u);
   for(int i=0; i<len; ++i){
       int v=edge[u][i];
       if(!pre[v]){
          dfs(v);
          low[u]=min(low[u], low[v]);
       }
       else if(!sccN[v])
          low[u] = min(low[u], pre[v]);
   }
   if(low[u]==pre[u]){
       ++cnt;
       while(1){
         int v=s.top();
         s.pop();
         sccN[v]=cnt;
         if(u==v) break;
       }
   }
}

int dfs2(int u){
    int len=edge[u].size();
    if(sccN[u]==1){//到达之后就不在进行任何搜索
       sum+=xx;
       return 1;
    }
    vis[u]=1;
    for(int i=0; i<len; ++i){
       int v=edge[u][i];
       if(!vis[v]){
           if(dfs2(v))
             return 1;
       }
    }
   return 0;
}

int main(){
   while(scanf("%d%d", &n, &m)!=EOF){
       dfs_clock=cnt=0;
       memset(pre, 0, sizeof(pre));
       memset(sccN, 0, sizeof(sccN));
       memset(vis, 0, sizeof(vis));
       memset(used, 0, sizeof(used));
       while(m--){
          int u, v;
          scanf("%d%d", &u, &v);
          edge[u].push_back(v);
       }
       for(int i=1; i<=n; ++i)
          if(!pre[i])
             dfs(i);
       int num=0;
       sum=0;
       used[1]=1;
       for(int i=1; i<=n; ++i){

          if(sccN[i]==1)
             ++num;
          else if(!used[sccN[i]]){
             memset(vis, 0, sizeof(vis));
             xx=sccN[i];
             used[sccN[i]]=1;
             dfs2(i);
          }
       }

       if(sum==(cnt+1)*cnt/2-1)//最后将能到达标号为1的连通分量的所有强连通分量的标号加起来
          printf("%d\n", num);
       else printf("0\n");
       for(int i=1; i<=n; ++i)
          edge[i].clear();
       while(!s.empty())
          s.pop();
   }
   return 0;
}
时间: 2025-01-18 07:48:52

poj2186Popular Cows(Kosaraju算法--有向图的强连通分量的分解)的相关文章

强连通分量的三个求法

这里主要谈及强连通分量(以下简称SCC,strongly connected component)三种常见的求法(以下涉及的图均为有向图),即Kosaraju.Tarjan和Gabow.三种算法背后的基础思想都是DFS,只是它们通过DFS获得了不同的信息.各位大哥大姐继续往下读之前,最好对DFS相关的概念和性质比较熟悉,例如,什么叫做<a title="DFS" href="http://en.wikipedia.org/wiki/Depth-firstsearch&q

数据结构——图

1 基本术语 有向图:图中的每条边都有方向的图叫有向图.此时,边的两个顶点有次序关系,有向边<u,v>成为从顶点u到顶点v的一条弧,u成为弧尾(始点),v成为弧头(终点),即有向图中弧<u,v>和弧<v,u>表示不同的两条边. 无向图:图中的每条边没有方向的图.边的两个顶点没有次序关系,无向图用边(u,v)表示对称弧<u,v>和<v,u>. 权:图中的边或弧上有附加的数量信息,这种可反映边或弧的某种特征的数据成为权. 网:图上的边或弧带权则称为网

图(网)的存储结构(数组存储表示即邻接矩阵、邻接表)

图(Graph)是一种非线性结构 图的特点(多对多),顶点之间的关系是任意的,图中任意两个顶点之间都可能相关,顶点的前驱和后继个数无限制. 图:数据元素间存在多对多关系的数据结构,加上一组基本操作构成的抽象数据类型. 图的基本术语   顶点:图中的数据元素. 弧:若 <v, w>∈VR,则 <v, w> 表示从 v 到 w 的一条弧,且称 v 为弧尾,称 w 为弧头,此时的图称为有向图.  G1 = (V1, A1)          V1 = {v1, v2, v3, v4} A

遍历-数据结构的问题 在线等!!!

问题描述 数据结构的问题 在线等!!! 完成图的深度优先遍历,并基于深度优先遍历,求: a. 无向图图的连通分量 b. 检测图是否有环 c. 有向图的强连通分量 解决方案 http://blog.csdn.net/todd911/article/details/9191481 解决方案二: http://www.cnblogs.com/tanhehe/archive/2013/05/20/3089765.html 解决方案三: http://blog.csdn.net/linxinyuluo/a

hdu1269迷宫城堡(判断有向图是否是一个强连通图)

/* 题意: 给你一个图,求这个有向图示否是一个强连通图(每两个节点都是可以相互到达的)! 思路1:按正向边dfs一遍,将经过的节点计数,如果记录的节点的个数小于n,那么就说明图按照正向边就不是连同的,所以就不是强连通图! 然后按照反向边再进行另一个dfs,同样对经过的节点的个数进行计数,如果个数==n则说明正向遍历和反响遍历都是连通的!那么整个图就是强连通的图! 思路2:直接套用tarjan算法,求出每一个节点所对应的缩点的值, 如果缩点的个数==1,那么证明就会只有一个强连通分量!也就是强连

算法系列15天速成——第十四天 图【上】

       今天来分享一下图,这是一种比较复杂的非线性数据结构,之所以复杂是因为他们的数据元素之间的关系是任意的,而不像树那样 被几个性质定理框住了,元素之间的关系还是比较明显的,图的使用范围很广的,比如网络爬虫,求最短路径等等,不过大家也不要胆怯, 越是复杂的东西越能体现我们码农的核心竞争力.               既然要学习图,得要遵守一下图的游戏规则. 一: 概念        图是由"顶点"的集合和"边"的集合组成.记作:G=(V,E): <1

C#数据结构与算法揭秘十

这篇文章,我们来讨论图的相关知识. 一.究竟什么图装结构了,所谓的图是图状结构简称图,是另一种非线性结构,它比树形结构更复杂.树形结构中的结点是一对多的关系,结点间具有明显的层次和分支关系.每一层的结点可以和下一层的多个结点相关,但只能和上一层的一个结点相关.而图中的顶点(把图中的数据元素称为顶点)是多对多的关系,即顶点间的关系是任意的,图中任意两个顶点之间都可能相关.也就是说,图的顶点之间无明显的层次关系,这种关系在现实世界中大量存在.因此,图的应用相当广泛,在自然科学.社会科学和人文科学等许

基本数据结构(算法导论)与python

Stack, Queue Stack是后进先出, LIFO, 队列为先进先出, FIFO 在python中两者, 都可以简单的用list实现, 进, 用append() 出, Stack用pop(), Queue用pop(0), pop的时候注意判断len(l)  对于优先队列, 要用到前面讲到的堆 链表和多重数组 这些数据结构在python中就没有存在的价值, 用list都能轻松实现 散列表 为了满足实时查询的需求而产生的数据结构, 查询复杂度的期望是O(1), 最差为O(n) 问题描述, 对

求教大神关于meanshift算法

问题描述 求教大神关于meanshift算法 请问大神 怎么将分解视频帧的程序与meanshift跟踪算法程序结合在一起 解决方案 http://www.cnblogs.com/liqizhou/archive/2012/05/12/2497220.html 解决方案二: MeanShift算法(一)meanshift算法Meanshift算法浅酌