L3-003. 社交集群-图的连通分支数&&并查集-天梯赛练习题

L3-003. 社交集群

在社交网络平台注册时,用户通常会输入自己的兴趣爱好,以便找到和自己兴趣相投的朋友。有部分兴趣相同的人们就形成了“社交集群”。现请你编写程序,找出所有的集群。

输入格式:

输入的第一行给出正整数N(<=1000),即社交网络中的用户总数(则用户从1到N编号)。随后N行,每行按下列格式列出每个人的兴趣爱好:

Ki: hi[1] hi[2] ... hi[Ki]

其中Ki(>0)是第i个人的兴趣的数量,hi[j]是第i个人的第j项兴趣的编号,编号范围为[1, 1000]内的整数。

输出格式:

首先在第一行输出整个网络中集群的数量,然后在第二行按非递增的顺序输出每个集群中用户的数量。数字间以1个空格分隔,行首尾不得有多余空格。

输入样例:
8
3: 2 7 10
1: 4
2: 5 3
1: 4
1: 3
1: 4
4: 6 8 1 5
1: 4
输出样例:
3
4 3 1

解题思路

想到两种,都实现了一下,都能AC

思路一:图的连通分支数。

这里求无向图的连通分支数用了有向图连通分支数的代码,原因是最近刚做了一道有向图连通性的题目,默了一遍代码巩固一下,上个题有向图的连通分支数代码有详细的注释,链接http://blog.csdn.net/ly59782/article/details/65442078,无向图的连通分支数很简单不再重写。主要说一下思路。

将兴趣与人连边,为了不混淆,人的编号是1~n兴趣的编号是n+1~2000。

然后求图的连通分支数,即为集群数。


思路二:并查集。

将每个人挂在他的兴趣下面,可重复挂,然后将每个兴趣下面的人做一个并查集,最后统计父亲节点的个数以及没个分支的人数。详见代码




代码1:统计图的连通分支数

#include <iostream>
#include <cstdio>
#include <map>
#include <cmath>
#include <string.h>
#include <algorithm>
#include <set>
#include <sstream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;

const int maxn = 2000+10;
const int INF = 0x3f3f3f3f;


vector<int> G[maxn];
stack<int> s;
int pre[maxn];
int lowlink[maxn];
int sccno[maxn];
int dfsLock;
int sccCnt;
int ans[maxn];///第i个集群的人数
int n;///n个人,编号1到n

void dfs(int u){
    pre[u] = lowlink[u] = ++dfsLock;

    s.push(u);
    for(int i = 0;i<G[u].size(); ++i){
        int v = G[u][i];
        if(!pre[v]){
            dfs(v);
            lowlink[u] = min(lowlink[u],lowlink[v]);
        }
        else if(!sccno[v]){
            lowlink[u] = min(lowlink[u],pre[v]);
        }
    }

    if(pre[u] == lowlink[u]){
        sccCnt++;
        while(1){
            int x = s.top();s.pop();
            if(x>=1 && x<=n) ans[sccCnt] ++;
            if(x == u) break;
        }
    }
}


int main()
{
    scanf("%d",&n);
    for(int i = 1;i<=n;++i){
        int m,t;
        scanf("%d:",&m);
        while(m>0){
            m--;
            scanf("%d",&t);
            G[t+n].push_back(i);
            G[i].push_back(t+n);///兴趣的编号为n+1~2000
        }
    }

    for(int i = 1; i<=n; ++i){
        if(!pre[i]) dfs(i);
    }
    sort(ans+1,ans+1+sccCnt);
    printf("%d\n",sccCnt);
    for(int i =sccCnt;i>0;--i){
        printf("%d",ans[i]);
        if(i!=1) printf(" ");
    }


    return 0;
}




代码2:并查集

#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#define maxn 1010
using namespace std;

vector<int> G[maxn];
int f[maxn];
int fsize[maxn];

int findp(int x){
    int xp = x;
    while(xp!=f[xp]){
        xp = f[xp];
    }
    f[x] = xp;
    return xp;
}


int main()
{
    int n;
    scanf("%d",&n);
    for(int i = 1;i<=n;++i){
        int m,t;
        scanf("%d:",&m);
        for(int j = 0; j<m;++j){///编号为t的兴趣下面挂着对他感兴趣的人
            scanf("%d",&t);
            G[t].push_back(i);
        }
    }

    ///初始化
    for(int i = 0;i<maxn;++i){
        f[i] = i;
        fsize[i] = 1;
    }

    for(int i = 1;i<maxn;++i){
        if(G[i].size() == 0) continue;
        int fa = findp(G[i][0]);
        for(int j = 1; j<G[i].size();++j){
            int fb = findp(G[i][j]);
            if(fa != fb){
                f[fb] = fa;
                fsize[fa] += fsize[fb];
            }
        }
    }

    int cnt = 0;
    vector<int> ans;
    for(int i = 1;i<=n;++i){
        if(f[i]==i)
        {
            cnt++;
            ans.push_back(fsize[i]);
        }
    }

    sort(ans.begin(),ans.end());

    printf("%d\n",cnt);
    for(int i = cnt-1; i>=0; --i){
        printf("%d",ans[i]);
        if(i!=0) printf(" ");
    }

    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值