poj 2186 Popular Cows (Tarjan 强连通分量+压缩点)


考察算法:强连通分量+压缩点。


先说明下题目的大意。

有一群牛在相互崇拜。崇拜是单向的。且崇拜是可传递的,即a->b,b->c,可以得到a->c。

分别告诉你总共有n头牛,m条彼此间的崇拜关系,让你求出有多少头牛是其他牛都崇拜的,这里称之为明星牛~~。

思路:

要求出明星牛的个数,可以很轻松的想到作为明星牛,是不能崇拜其他牛的~,但是问题来了。

然后明星牛的个数一定是1嘛?经过博主多次WR发现不是的快哭了。有一种情况就是这个明星牛群是一个有向环,有向环?肯定要想到强连通分量!他们彼此相互崇拜。

同理,整个牛群中有可能存在多个有向环。真的是搞不懂难道他们还自己崇拜自己?

到这里,这个题的大概思路就出来了。求出牛群中有多少个强连通分量,把每个强连通分量都压缩成一个点,整个一个牛群间的关系就可以转化成一个有向无环图,即为DAG。

然后找明星牛,明星牛所在的压缩点的出度一定是为零的~。

如果有多个压缩点的出度都为零了呢?我一开始没有考虑这里,又WR了好几次。当有多个压缩点的出度为零,那么很显然,整个牛群中没有明星牛!

pass:这个题的数据poj出的有点弱,好像数据里没有考虑孤立结点的情况,给一组数据,博主没有过,但是提交的时候AC了,下面的代码是改正过的代码。

这里求强连通分量我用的是trajan算法

input
4 3
1 2
2 1
1 3
/*4是孤立节点*/
output
0

下面是完整的代码

#include<cstdio>
#include<cstring>
#include<stack>
using namespace std;
const int vMaxsize = 10050;
const int eMaxsize = 50050;
typedef struct
{
    int s,e,next;
}node;

node edge[eMaxsize];

int head[vMaxsize],dfn[vMaxsize],low[vMaxsize],color[vMaxsize];

bool vis[vMaxsize],judge[vMaxsize];

int e_num,scc,index;

void addedge(int a,int b)/*加边*/
{
    edge[e_num].s = a;
    edge[e_num].e = b;
    edge[e_num].next = head[a];
    head[a] = e_num++;
}

void getmap(int n,int m)/*建图*/
{
    memset(head,-1,sizeof(head));
    memset(vis,false,sizeof(vis));
    memset(dfn,-1,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(judge,false,sizeof(judge));
    e_num = 0;
    while(m--)
    {
        int a,b;
        scanf("%d %d",&a,&b);
        judge[a] = true;
        judge[b] = true;
        addedge(a,b);
    }
}

stack<int>s;

void trajan(int u)/*求强连通分量*/
{
    dfn[u] = low[u] = index++;
    vis[u] = true;
    s.push(u);
    for(int i = head[u] ; i != -1 ; i = edge[i].next)
    {
        int v = edge[i].e;
        if(dfn[v] == -1)
        {
            trajan(v);
            if(low[u] > low[v]) low[u] = low[v];
        }
        else if( vis[v] && low[u] > dfn[v])
        {
            low[u] = dfn[v];
        }
    }
    if(low[u] == dfn[u])
    {
        int j;
        scc++;
        do
        {
            j = s.top();
            s.pop();
            vis[j] = false;
            color[j] = scc;/*不同的强连通分量染色*/
        }while(u!=j);
    }
}

void solve(int n)
{
    scc = 0;
    for(int i = 1; i <= n ; i++)
    {
       if(dfn[i] == -1)
       {
           trajan(i);
       }
    }
}

int main()
{
    int n,m;
    while(scanf("%d %d",&n,&m)!=EOF)
    {
        int in[vMaxsize],out[vMaxsize];
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        getmap(n,m);
        solve(n);
        int key = 0;
        for(int i = 1; i <= n ; i++)/*确定是否有孤立节点*/
        {
            if(judge[i] == false)
            {
                key++;
            }
        }
        for(int i = 0 ; i < e_num ; i++)
        {
            if(color[edge[i].s] != color[edge[i].e])
            {
                out[color[edge[i].s]]+=1;/*存储各个压缩点的出度跟入度*/
                in[color[edge[i].e]]+=1;
            }
        }
        int sum = 0;
        bool flag = false;
        if(key != 0)/*有孤立节点,则一定没有明星牛*/
        {
            sum = 0;
        }
        else if(scc == 1)/*整个牛群只有一个强连通分量,一定是彼此相互崇拜的*/
        {
            sum = n;
        }
        else
        {
            for(int i = 1; i <= scc ; i++)
            {
                if(out[i] == 0 && in[i] != 0)
                {
                    for(int j = 1; j <= n ; j++)
                    {
                        if(color[j] == i)
                        {
                            sum++;
                        }
                    }
                    if(!flag)/*标记,是否只有一个压缩点的出度为零*/
                    {
                        flag = true;
                    }
                    else
                    {
                        sum = 0;
                        break;
                    }
                }
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

下面给一些数据,可以去测测自己的code

input
3 3
1 2
2 3
3 1

3 3
1 2
2 1
2 3

5 4
1 4
2 4
3 4
5 4

5 5
1 2
2 3
3 1
1 4
4 5

5 6
1 2
2 3
3 1
1 4
4 5
5 3

2 2
1 2
2 1

3 2
1 2
2 1

6 6
1 2
2 3
3 1
1 4
4 5
5 3

5 6
1 2
2 3
3 1
1 4
4 5
5 4

5 7
4 1
1 2
2 3
3 1
1 4
4 5
5 4

5 6
1 2
2 3
3 1
1 4
4 5	
5 1

7 9
1 2
2 3
3 1
4 5
5 6
6 4
4 7
7 1
1 7

6 6
1 2
2 3
3 1
4 5
5 6
6 4

4 4
1 2
2 3
3 1
1 4

4 4
1 2
2 3
3 1
4 1

5 6
1 2
2 3
3 1
5 1
5 4
3 4

7 9
1 2
2 3
3 1
5 1
5 4
3 4
4 7
7 6
6 4

output
3
1
1
1
5
2
0
0
2
5
5
4
0
1
3
1
3



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值