并查集
多少张桌子
时间限制:2000/1000 MS(Java/其他) 内存限制:65536/32768 K(Java/其他)
问题描述
今天是伊格内修斯的生日。他邀请了很多朋友。现在是晚餐时间。伊格内修斯想知道他至少需要多少张桌子。你必须注意,并不是所有的朋友都认识对方,所有的朋友都不想和陌生人呆在一起。
这个问题的一个重要规则是,如果我告诉你 A 认识 B,而 B 认识 C,这意味着 A、B、C 彼此认识,所以他们可以呆在一张桌子上。
例如:如果我告诉你 A 知道 B,B 知道 C,D 知道 E,所以 A、B、C 可以留在一张桌子上,而 D、E 必须留在另一张桌子上。所以 Ignatius 至少需要 2 张桌子。
输入
输入以整数 T(1<=T<=25) 开头,表示测试用例的数量。然后是 T 测试用例。每个测试用例都以两个整数 N 和 M(1<=N,M<=1000) 开头。N表示好友数量,好友从1到N标记。然后是 M 行。每行由两个整数 A 和 B(A!=B) 组成,这意味着朋友 A 和朋友 B 彼此认识。两个案例之间将有一个空行。
输出
对于每个测试用例,只需输出 Ignatius 至少需要多少个表即可。不要打印任何空白。
示例输入
2
5 3
1 2
2 3
4 5
5 1
2 5
示例输出
2
4
代码示例:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1010;
int fa[N], p[N];
inline void init(int n) {
	for (int i = 1; i <= n; i++) {
		fa[i] = i;
		p[i] = 1;
	}
}
int find(int x) {
	if (x == fa[x])
		return x;
	else
		return fa[x] = find(fa[x]);//路径压缩
}
inline void merge(int i, int j) {
	int x = find(i), y = find(j);
	if (p[x] < p[y])
		fa[x] = y;
	else
		fa[y] = x;
	if (p[x] == p[y] && x != y)//按秩合并
		p[x]++;
}
int main()
{
	int n,t,m,x,y;
	cin >> t;
	while (t--) {
		
		cin >> n >> m;
		init(n);
		for (int i = 1; i <= m; i++) {
			cin >> x >> y;
			merge(x, y);
		}
		int ans = 0;
		for (int i = 1; i <= n; i++)
			if (i == fa[i])
				ans++;
		cout << ans << endl;
	}
	return 0;
}
扩展题:
1703 -- Find them, Catch them (poj.org)
2236 -- Wireless Network (poj.org)
2492 -- A Bug's Life (poj.org)
1988 -- Cube Stacking (poj.org)
嫌疑人:
代码示例:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 30010;
int fa[N], p[N];
int num[N];
inline void init(int n) {
	for (int i = 0; i < n; i++)
	{
		fa[i] = i;
		p[i] = 0;
		num[i] = 1;
	}
}
int find(int x) {
	if (x == fa[x])
		return x;
	else
		return fa[x] = find(fa[x]);
}
inline void merge(int i, int j) {
	int x = find(i), y = find(j);
	if (p[x] < p[y]) {
		fa[x] = y;
	}
	else {
		fa[y] = x;
	}
	if (p[x] == p[y] && x != y)
		p[x]++;
}
int main()
{
	ios::sync_with_stdio(false);
	int n, m;
	int t;
	int x, y;
	while (cin >> n >> m && n || m) {
		int ans = 0;
		init(n);
		for (int i = 0; i < m; i++) {
			cin >> t;
			cin >> x;
			for (int j = 1; j < t; j++)
			{
				cin >> y;
				merge(x, y);
			}
		}
		for (int i = 0; i < n; i++)
		{
			
			if (find(i) == fa[0])
				ans++;
		}
		
		cout << ans << endl;
	}
	return 0;
}
在给定的代码中,find 函数采用了路径压缩的方法。路径压缩的目的是通过直接更新所有经过的节点到根节点的父指针,使对相同元素的后续 find 调用更加高效。
在 if (find(i) == fa[0]) 语句中,你正在检查元素 i 是否属于与元素 0 同一个集合。如果将 find(i) 替换为 fa[i],那么它只是简单地检查 i 的直接父节点是否是 0,而不考虑路径压缩的影响。这可能导致不正确的结果,因为两个节点可能在路径压缩后具有相同的根节点,但它们的直接父节点不同。
在语句 if (find(i) == fa[0]) 中,find(i) 调用会更新父指针(执行路径压缩),然后将结果与 fa[0] 进行比较。如果将 find(i) 更改为 fa[i],它将不会有相同的效果,因为 fa[i] 是不经过路径压缩的父指针。
因此,在 if 语句中直接使用 fa[i] 将无法正常工作,因为它不会考虑 find 函数执行的路径压缩。路径压缩的目的是优化未来对 find 的调用,使树更加平衡,减少后续 find 操作的时间复杂度。
找到他们,抓住他们
1703 -- Find them, Catch them (poj.org)
描述
塔杜市警察局决定结束混乱,并采取行动铲除该市的两个帮派,Gang Dragon 和 Gang Snake。但是,警方首先需要确定罪犯属于哪个团伙。现在的问题是,给定两个罪犯;他们属于同一个氏族吗?您必须根据不完整的信息做出判断。(因为歹徒总是在秘密行动。
假设 N (N <= 10^5) 名罪犯目前在塔杜市,编号从 1 到 N。当然,其中至少有一个属于 Gang Dragon,Gang Snake 也是如此。您将依次获得 M (M <= 10^5) 条消息,分为以下两种:
\1. D [a] [b],其中 [a] 和 [b]
是两个罪犯的数字,他们属于不同的帮派。
\2. A [a] [b],其中 [a] 和 [b]
是两个罪犯的人数。这需要您决定 a 和 b 是否属于同一个帮派。
输入
输入的第一行包含一个整数 T (1 <= T <= 20),即测试用例的数量。然后是 T 病例。每个测试用例都以包含两个整数 N 和 M 的行开头,后跟 M 行,每个行包含一条消息,如上所述。
输出
对于每种情况下的每条消息“A [a] [b]”,您的程序应根据之前获得的信息做出判断。答案可能是“在同一个帮派中”,“在不同的帮派中”和“还不确定”之一。
示例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
示例输出
Not sure yet.
In different gangs.
In the same gang.
代码示例:
/*
solution:
	并查集。
	这道题有点类似poj1182(食物链),两题的关键点都在于如何维护不同集合的关系。并查集的功能
	正好与之相反,是维护相同集合间的关系。如何用并查集来实现不同集合的维护呢?方法是改变集
	内元素的类型。本来merge(a,b)表示a,b属于同一个集合,现在用merge(a,b+n)来表示a,b属
	于不同集合,即a属于第一个集合,b属于第二个集合。同理merge(a+n,b)表示a属于第二个集合
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 1e5 + 5;
int n, m;
int fa[N * 2], p[N * 2];
void init(int n)
{
	for (int i = 1; i <= n; i++)
	{
		fa[i] = i;
		p[i] = 0;
	}
}
int find(int x)
{
	if (x == fa[x])
		return x;
	else
		return fa[x] = find(fa[x]);
}
void merge(int x, int y)
{
	x = find(x);
	y = find(y);
	if (p[x] < p[y])
		fa[x] = y;
	else
		fa[y] = x;
	if (p[x] == p[y] && x != y)
		p[x]++;
}
bool same(int x, int y)
{
	return find(x) == find(y);
}
int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &n, &m);
		init(2 * n);
		char op;	int a, b;
		for (int i = 1; i <= m; i++)
		{
			scanf("\n%c%d%d", &op, &a, &b);
			if (op == 'D')
			{
				merge(a, b + n);
				merge(a + n, b);
			}
			else
			{
				if (same(a, b + n) || same(a + n, b))	printf("In different gangs.\n");
				else if (same(a, b) || same(a + n, b + n))	printf("In the same gang.\n");
				else	printf("Not sure yet.\n");
			}
		}
	}
	return 0;
}

 
                
             浙公网安备 33010602011771号
浙公网安备 33010602011771号