洛谷P1892 团伙(并查集)--- 练习数据结构中

P1892 [BalticOI 2003] 团伙

题目描述

现在有 nnn 个人,他们之间有两种关系:朋友和敌人。我们知道:

  • 一个人的朋友的朋友是朋友
  • 一个人的敌人的敌人是朋友

现在要对这些人进行组团。两个人在一个团体内当且仅当这两个人是朋友。请求出这些人中最多可能有的团体数。

输入格式

第一行输入一个整数 nnn 代表人数。

第二行输入一个整数 mmm 表示接下来要列出 mmm 个关系。

接下来 mmm 行,每行一个字符 optoptopt 和两个整数 p,qp,qp,q,分别代表关系(朋友或敌人),有关系的两个人之中的第一个人和第二个人。其中 optoptopt 有两种可能:

  • 如果 optoptoptF,则表明 pppqqq 是朋友。
  • 如果 optoptoptE,则表明 pppqqq 是敌人。

输出格式

一行一个整数代表最多的团体数。

输入输出样例 #1

输入 #1

6
4
E 1 4
F 3 5
F 4 6
E 1 2

输出 #1

3

说明/提示

对于 100%100\%100% 的数据,2≤n≤10002 \le n \le 10002n10001≤m≤50001 \le m \le 50001m50001≤p,q≤n1 \le p,q \le n1p,qn

题解

#include<iostream>
using namespace std;

int n, m, p, q;
char opt;
int a[1005]; //并查集的父节点存储
int b[1005]; //存储每个人的敌人

//查找、路径压缩
inline int find(int x) {
	if (x == a[x]) return x;
	return a[x] = find(a[x]);
}
//合并
inline void join(int x, int y) {
	x = find(x), y = find(y);
	if (x != y)
		a[x] = y;
}
int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		a[i] = i;
	while (m--) {
		cin >> opt >> p >> q;
		if (opt == 'F') {
			join(p, q);
		}
		else {
			if (!b[p]) b[p] = q;//标记为敌人
			else join(b[p], q);//敌人的敌人就是朋友

			if (!b[q]) b[q] = p;
			else join(b[q], p);
		}
	}
	int count = 0;
	for (int i = 1; i <= n; i++) {
		if (find(i) == i) {
			count++;
		}
	}
	cout << count;
	return 0;
}

代码的主要功能解析:

  1. 数据结构:

    • a[] 数组用于并查集的父节点存储
    • b[] 数组用于存储每个人的敌人
  2. 核心函数:

    • find(x):查找元素 x 的根节点,并进行路径压缩优化
    • join(x, y):将 x 和 y 合并到同一个集合中(表示他们是朋友)
  3. 主逻辑:

    • 初始化:每个人都是自己的朋友(a[i] = i)
    • 处理 m 条关系指令:
      ‘F’ 表示 p 和 q 是朋友,直接合并
      其他字符(通常是 ‘E’ 表示敌人):
      如果 p 没有敌人,则记录 q 为 p 的敌人
      如果 p 已有敌人,则 p 的敌人与 q 成为朋友(敌人的敌人是朋友)
      对 q 执行同样的操作
  4. 结果计算:

    • 统计有多少个独立的朋友圈(根节点的数量)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值