【图论】tarjan缩点:poj2186 Popular Cows

tarjan缩点

缩点是图论中常用的技巧,当路径上贡献具有传导性时,可以将一个强连通分量缩成一个新点,因为一个强连通分量内的点可以互相到达。强连通分量内的点的个数可以通过染色记录,具有同一种颜色的点的个数即为该强连通分量内点的个数。

例题:poj2186 Popular Cows

告诉你有n头牛,m个崇拜关系,并且崇拜具有传递性,如果a崇拜b,b崇拜c,则a崇拜c,求最后有几头牛被所有牛崇拜。

Sample Input

3 3

1 2

2 1

2 3

Sample Output

1

题解:

先考虑整张图无环时(DAG)的情况,当存在一头牛被所有牛崇拜时,只需考虑出度为0的点的个数(出度为0的点的个数一定大于等于1),若为1,则有解,若大于1,则无解;
若图中有环,则用tarjan将图中强连通分量找出后,对于点u相连的点v,若他们的颜色相同,说明他们处于同一个强连通分量,视作u,v在一个超级点中,不统计在u的出度内;若他们的颜色不同,则说明这两个点属于不同的强连通分量中,即可让u的度数加1.
统计完成后,对于每种颜色(即所有强连通分量)统计度数为0的点,即可得到被所有牛崇拜的“超级点”个数。注意,若该点为点数大于1的强连通分量构成的“超级点”,则要将该强连通分量内的点数加入答案中,而不是加1.

#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;

int n,m;
const int maxn=10010;
int dfn[maxn],low[maxn],cnt[maxn],color[maxn],degree[maxn],vis[maxn];
int tot;
int st[maxn],top;
int tmp,ans;
int deep;
vector<int> g[maxn];

void tarjan(int u)
{
    dfn[u]=++deep;
    low[u]=deep;
    vis[u]=1;
    st[++top]=u;
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
        {
            if(vis[v])
            low[u]=min(low[u],low[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        color[u]=++tot;
        vis[u]=0;
        while(st[top]!=u)
        {
            color[st[top]]=tot;
            vis[st[top--]]=0;
        }
        top--;
    }
}

int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        memset(vis,0,sizeof(vis));
        memset(dfn,0,sizeof(dfn));
        memset(color,0,sizeof(color));
        memset(degree,0,sizeof(degree));
        memset(low,0,sizeof(low));
        memset(cnt,0,sizeof(cnt));
        memset(st,0,sizeof(st));
        for(int i=1;i<=m;i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            g[x].push_back(y);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])tarjan(i);
        for(int i=1;i<=n;i++)
        {
            int sz=g[i].size();
            for(int j=0;j<sz;j++)
            {
                int v=g[i][j];
                if(color[i]!=color[v])
                    degree[color[i]]++;
            }
            cnt[color[i]]++;
        }
        for(int i=1;i<=tot;i++)
            if(degree[i]==0) tmp++,ans=cnt[i];
        if(tmp>1) printf("0\n");
        else printf("%d\n",ans); 
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/JWizard/p/11753923.html