当前位置:首页 > 题解目录 > 正文内容

【题解】团伙

亿万年的星光7个月前 (06-06)题解目录17726

【题目描述】

在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足:
1、我朋友的朋友是我的朋友;
2、我敌人的敌人是我的朋友;
所有是朋友的人组成一个团伙。告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多少个团伙?

【输入描述】

第1行为n和m,1<n<1000,1<=m<=100 000;
以下m行,每行为p x y,p的值为0或1,p为0时,表示x和y是朋友,p为1时,表示x和y是敌人。

【输出描述】

一个整数,表示这n个人最多可能有几个团伙。

【样例输入】

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

【样例输出】

3


【题目分析】


  1. 朋友的朋友是朋友(传递性)。

  2. 敌人的敌人是朋友(敌人的传递性)。

我们需要根据给定的朋友和敌人关系,将所有可能的朋友合并到同一个集合(团伙)中,最后统计集合的数量。

[解题思路]

  1. 并查集数据结构:

    • 使用并查集来维护朋友关系。

    • 每个节点的根节点代表它所属的团伙。

  2. 处理朋友关系(p=0):

    • 直接合并 x 和 y 所在的集合。

  3. 处理敌人关系(p=1):

    • 如果 x 已经有敌人,那么 y 应该和 x 的敌人是朋友。

    • 如果 y 已经有敌人,那么 x 应该和 y 的敌人是朋友。

    • 如果 x 和 y 是敌人,那么 x 的敌人集合和 y 的敌人集合需要合并。

    • 具体来说:

    • 可以用一个数组 enemy 来记录每个人的敌人。

  4. 统计团伙数量:

    • 遍历所有人,统计不同根节点的数量。


【参考答案】

#include<iostream>
using namespace std;
int rank[100],fa[100];
int enemy[100];//enemy[i]表示i的敌人 

//初始化 
void init(int n)
{
    for (int i = 1; i <= n; ++i)
    {
        fa[i] = i;
        rank[i] = 1;
    }
}
//查找 
int find(int x)
{
    if(x == fa[x])
        return x;
    else{
        fa[x] = find(fa[x]);        //父节点设为根节点
        return fa[x];         //返回父节点
    }
}

//合并 
void merge(int x,int y) {
	x=find(x);							//寻找 x的根节点 
	y=find(y);							//寻找 y的根节点 
	if(x==y) return ;					//如果 x和 y的根节点一致,说明他们共属同一集合,则不需要合并,直接返回;否则,执行下面的逻辑
	if(rank[x]>rank[y]) 				//如果 x的高度大于 y,则令 y的上级为 x
		fa[y]=x;	
	else {								
		if(rank[x]==rank[y]) 		   //如果 x的高度和 y的高度相同,则令 y的高度加1
			rank[y]++;	
		fa[x]=y;						//让 x的上级为 y
	}
}

int main(){
	int n,m;
	cin>>n>>m;
	init(n);//初始化
	for(int i=0;i<m;i++){
		int p,x,y;
		cin>>p>>x>>y;
		if(p==0){
			//如果x和y是朋友,直接合并
			merge(x,y); 
		} else{
			//x和y是敌人
			if(enemy[x]==0){
				enemy[x]=y;
			} else{
				//x的敌人是enemy[x],
				//y应该和enemy[x]是朋友
				merge(y,enemy[x]); 
			}
			
			if(enemy[y]==0){
				enemy[y]=x;
			}else{
				//y的敌人是enemy[y]
				//x 应该和 enemy[y] 是朋友
				merge(x,enemy[y]); 
			}
		}
	} 
	//统计团伙数量(不同根的数量)
	int count=0;
	for(int i=1;i<=n;i++){
		if(find(i)==i){
			count++;
		}
	} 
	cout<<count;
	return 0;
	
}


扫描二维码推送至手机访问。

版权声明:本文由青少年编程知识记录发布,如需转载请注明出处。

分享给朋友:

相关文章

【题解】最少操作使数组递增

【题目描述】给你一个整数数组 nums (下标从 0 开始)。每一次操作中,你可以选择数组中一个元素,并将它增加 1 。比方说,如果 nums = [...

【题解】开关灯(2)

1.开关灯(light.cpp)【题目描述】某实验室共有n盏灯,灯的编号为1~n,每盏灯的初始状态是关闭的。现在有m位学生,每位学生可以前去抽取一张带数字的卡片,其数字为Ai,然后依次将自己手中的数字...

【题解】柠檬水找零

【题目描述】在柠檬水摊上,每一杯柠檬水的售价为 5 美元。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。每位顾客只买一杯柠檬水,然后向你...

因子分解

【题目描述】输入一个数,输出其素因子分解表达式。【输入描述】输入一个整数 n (2≤n<100)。【输出描述】输出该整数的因子分解表达式。表达式中各个素数从小到大排列。如果该整数可以分解出因子a...

【题解】密码截获

【题目描述】Catcher是MCA国的情报员,他工作时发现敌国会用一些对称的密码 进行通信,比如像这些ABBA,ABA,A,123321,但是他们有时会在开始或结束时加入一些无关的字符以防止别国破解。...

【题解】转换的问题

【题目描述】那么,问题来了:输入一个十进制数N,将它转换成R进制数输出。【输入描述】输入数据包含多个测试实例,每个测试实例包含两个整数N(32位整数)和R(2<=R<=16, R<&...