poj 3180 The Cow Prom(奶牛舞会)

在一场特别的舞会上,奶牛们通过绳索相连跳舞,形成有趣的强连通分量。本篇博客介绍了一个算法问题,通过两次深度优先搜索(DFS)来找出所有成功跳舞的奶牛组合,即强连通分量的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【问题描述】

  约翰的N只奶牛非常兴奋,因为这是个舞会之夜!他们穿上礼服和新鞋子,别上鲜花,他们要表演圆舞曲。圆舞在一个圆形的水池上进行。奶牛们围在池边站好,顺时针顺序由1到N编号。每只奶牛都面对水池,这样她就能看到其他的每只奶牛。

  为了跳这种圆舞,他们找了M条绳索。若干只奶牛的蹄子上握着绳索,绳索沿着顺时针方向绕过水池,另一端则捆在另一些奶牛的身上。这样,一些奶牛就可以牵引另一些奶牛。有的奶牛可能握有很多绳索,也有的奶牛可能一条绳索都没有。

  对于一只奶牛,比如说贝茜,她的圆舞跳的是否成功,可以这样检验:沿着她牵引的绳索,找到她牵引的奶牛,沿着这只奶牛牵引的绳索,又找到一只被牵引的奶牛,如此下去,若最终能回到贝茜,则她的圆舞跳的成功,因为这一个环上的奶牛可以逆时针牵引而跳起旋转的圆舞。如果这样的检验无法完成,那她的圆舞是不成功的。

  如果两只成功圆舞的奶牛有绳索相连接,那他们可以属于一个组合。给出每一条绳索的描述,请找出,成功跳了圆舞的奶牛有多少个组合?

【输入格式】

  第一行输入N和M,接下来的M行,每行两个整数A和B,表示A牵引着B。

【输出格式】

  成功跳圆舞奶牛组合数据。

【输入样例】

5 4
2 4
3 5
1 2
4 1

【输出样例】

1

【样例解释】

1 2 4这三只牛同属于一个成功跳了圆舞的组合,而3,5两只奶牛没有跳成功所谓圆舞.

【数据范围】

2<=N<=10000
2<=M<=50000

【来源】

poj 3180

分析:
从此题目中可以看出这是一道经过包装之后的强连通分量题目,牵引的绳索可以看成有向图强连通分量的边,而顺逆时针可以看成是方向,圆舞的组合就是强连通分量,题目就是要求你求出强连通分量的数目,(注意要求节点数大于2),从数据范围来看,还是应采用两次DFS的方法进行解答。
代码如下:

#include<cstdio>
#include<vector>
#include<cstring>
#define maxn 10005
using namespace std;
bool vis[maxn];
int belong[maxn]={0},scc,n,m,sz[maxn]={0};
vector<int>g[maxn],gr[maxn],vs;
void init(){
     scanf("%d%d",&n,&m);
     for(int i=1;i<=m;i++){
         int u,w;
         scanf("%d%d",&u,&w);
         g[u].push_back(w);                 //正向图    
         gr[w].push_back(u);                //反向图 
     }
}
void DFS(int i){
     vis[i]=1;
     for(int k=0;k<g[i].size();k++){
         int j=g[i][k];
         if(!vis[j])DFS(j);
     }
     vs.push_back(i);                             //后序遍历 
}
void DFS2(int i,int scc){
     belong[i]=scc;
     sz[scc]++;
     for(int k=0;k<gr[i].size();k++){
         int j=gr[i][k];
         if(belong[j])continue;
         DFS2(j,scc);
     }
}
void find_scc(){
     memset(vis,0,sizeof(vis));

     for(int i=1;i<=n;i++)
         if(!vis[i])DFS(i);

     scc=0;
     memset(belong,0,sizeof(belong));
     memset(sz,0,sizeof(sz));
     for(int i=vs.size()-1;i>=0;i--){
         int k=vs[i];
         if(!belong[k])DFS2(k,++scc);
     }     
}
int main(){
    init();
    find_scc();
    int ans=0;
    for(int i=1;i<=scc;i++)
        if(sz[i]>=2)ans++;
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值