POJ2186--Popular Cows(Korasaju+缩点)

来源:互联网 发布:逍遥模拟器网络不好 编辑:程序博客网 时间:2024/05/21 14:04

题目大意:有N头牛,他们都喜欢膜拜其他牛,有M种膜拜关系,问有多少头牛被其他所有的牛膜拜。

分析:这个问题的模型就是,给出一个有向图,有多少个顶点可以被其他所有顶点达到。在DAG(有向无环图)中,只有出度为0的

点,才能被其他所有点到达。由于无环,所以从任何点出发,都将终止于出度为0的点。

首先,我们求出所有的强连通分量。 这里我们用Korasaju算法,简单地说就是两次dfs。第一次dfs,先从任意一个顶点开始遍历所有尚未访问过

的点,并在回溯前给顶点标号,其实就是后序遍历所有节点啦~标号完成后,我们会发现越接近搜索树的叶子,顶点标号越小。然后,就是第二次dfs,

先将所有边反向,这种所有边反向的图,又称转置图。从标号最大的顶点作为起点,开始dfs,这样遍历到的顶点集合就构成了一个强连通分量。依次

dfs尚未访问的节点,就可以得到剩余的强连通分量。

很明显有这样一个事实,至多只有一个强连通分量满足条件。而在得到所有强连通分量的同时,我们还可以得到他们的拓扑序,也就是说只有拓

扑序的最后一个强连通分量才可能满足条件(不满足的情况,就是缩点后的DAG上有多于一个的出度为0的点)。

     接着,再将每个强连通分量缩成一个点,这样就形成了一个DAG了。缩点不一定要构成新图,给不同的强连通分量染不同颜色即可。

最后,我们只需要利用转置图判断这个强连通分量是否能到达所有点,能的话,那么从原图的其他顶点都能到达这个强连通分量,答案就是这个

强连通分量的顶点个数。


代码:

#include <cstdio>#include <vector>#include <cstring>#include <algorithm>using namespace std;const int maxn = 11111;vector<int> g[maxn];    //原图vector<int> rg[maxn];   //转置图vector<int> vs;         //后序遍历得到的顶点列表bool vis[maxn];int color[maxn];        //顶点v属于哪一个强连通分量int n, m;void dfs(int u) {    vis[u] = 1;    int len = g[u].size();    for(int i = 0; i < len; i++)        if(!vis[g[u][i]]) dfs(g[u][i]);    vs.push_back(u);}void rdfs(int u, int k) {    vis[u] = 1;    color[u] = k;       //这里就是缩点了    int len = rg[u].size();    for(int i = 0; i < len; i++)        if(!vis[rg[u][i]]) rdfs(rg[u][i], k);}int scc() {    memset(vis, 0, sizeof(vis));    vs.clear();    for(int i = 0; i < n; i++)        if(!vis[i]) dfs(i);    memset(vis, 0, sizeof(vis));    int k = 0;    for(int i = vs.size()-1; i >= 0; i--)   //从顶点标号最大的顶点开始第二次遍历,因为从0开始编号,所以减一        if(!vis[vs[i]]) rdfs(vs[i], k++);    return k;}int main() {    while(~scanf("%d%d", &n, &m)) {        for(int i = 0; i < m; i++) {            int u, v;            scanf("%d%d", &u, &v);            g[u-1].push_back(v-1);            rg[v-1].push_back(u-1);        }        int num = scc();        int u = 0, ans = 0;        for(int i = 0; i < n; i++)            if(color[i] == num-1)   //判断当前顶点是否属于最后一个强连通分量                u = i, ans++;        memset(vis, 0, sizeof(vis));        rdfs(u, 0);                 //利用转置图从最后一个强连通分量开始遍历所有定        for(int i = 0; i < n; i++)            if(!vis[i]) {           //只要有一个顶点没有被访问,则说明无解                ans = 0;                break;            }        printf("%d\n", ans);    }    return 0;}


0 0
原创粉丝点击