洛谷 P1892 [BOI2003] 团伙

题目链接:

https://www.luogu.com.cn/problem/P1892icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1892


题目:

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

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

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

输入格式:

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

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

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

  • 如果 opt 为 F,则表明 p 和 q 是朋友
  • 如果 opt 为 E,则表明 p 和 q 是敌人

输出格式:

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

例:

输入:

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

输出:

3

数据提示:

对于 100% 的数据,2 ≤ n ≤ 1000,1 ≤ m ≤ 5000,1 ≤ p , q ≤ n


思路:

首先看题,可以得知需要求得最多的团体数。看到团体/集合等词语时,我们应该反应过来考查的是并查集。那么我们可以拿出并查集模板了:

class Union {
	public:
		Union(int n) :fa(n + 1) {
			for (int i = 0; i < n; i++) {
				fa[i] = i;
			}
		}

		int find(int i) {
			return fa[i] = (fa[i] == i ? i : find(fa[i]));
		}

		void merge(int x, int y) {
			if (find(x) == find(y)) {
				return;
			}
			fa[find(x)] = find(y);
			return;
		}

		vector<int> fa;
};

首先用俩个整形n和num用于接收有多少个人和多少种关系。

接着循环num次,用op表示关系,per和tar表示俩人来接收每次都关系。(洛谷题目输入有问题,使用scanf有时会出现‘\n‘,‘\0‘等)。

因为在关系中存在朋友关系和敌对关系,但是敌人的敌人是朋友,所以这里要使用到反集的知识(没做前,我也不知道反集这东西)。

(不懂的看看这里 -> 并查集反集及应用-CSDN博客 )

对于朋友关系,在并查集中直接连通俩人即可。

对于敌人关系,我们让per与n+tar相连通,tar与n+per相连通即可。(因为敌人的敌人是朋友,我们让tar与n+per相连通,当tar的其他敌人与n+per相连通时,就实现了敌人的敌人是朋友)

然后,使用循环,判断该人的父结点是否是自己,若是,则ans加一,反正不变。

最后输出ans即可。


AC代码:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>

using namespace std;

class Union {
	public:
		Union(int n) :fa(n + 1) {
			for (int i = 0; i < n; i++) {
				fa[i] = i;
			}
		}

		int find(int i) {
			return fa[i] = (fa[i] == i ? i : find(fa[i]));
		}

		void merge(int x, int y) {
			if (find(x) == find(y)) {
				return;
			}
			fa[find(x)] = find(y);
			return;
		}

		vector<int> fa;
};

int n, num, per, tar;
char op;

int main() {

	scanf("%d", &n);
	Union u(2 * n);
	scanf("%d", &num);
	getchar();
	while (num--) {
		cin >> op >> per >> tar;
		if (op == 'E') {
			u.merge(n + per, tar);
			u.merge(n + tar, per);
		}
		else if (op == 'F') {
			u.merge(per, tar);
		}
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		if (u.find(i) == i) {
			ans++;
		}
	}
	printf("%d\n", ans);

	return 0;
}

如果我的文章对你有所帮助,不妨给我个关注何点赞吧。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值