Codeforces 300B Coach[并查集]

Codeforces 300B Coach[并查集]


题目链接:Codeforces 300B Coach

Time limit : 2000 ms / Memory limit : 262144 kb

Problem Statement
A programming coach has n students to teach. We know that n is divisible by 3. Let’s assume that all students are numbered from 1 to n, inclusive.
Before the university programming championship the coach wants to split all students into groups of three. For some pairs of students we know that they want to be on the same team. Besides, if the i-th student wants to be on the same team with the j-th one, then the j-th student wants to be on the same team with the i-th one. The coach wants the teams to show good results, so he wants the following condition to hold: if the i-th student wants to be on the same team with the j-th, then the i-th and the j-th students must be on the same team. Also, it is obvious that each student must be on exactly one team.
Help the coach and divide the teams the way he wants.

Input
The first line of the input contains integers n and m (3 ≤ n ≤ 48, 0 ≤ m≤ n(n-1)/2. Then follow m lines, each contains a pair of integers ai, bi (1 ≤ ai < bi ≤ n) — the pair ai, bi means that students with numbers ai and bi want to be on the same team.
It is guaranteed that n is divisible by 3. It is guaranteed that each pair ai, bi occurs in the input at most once.

Output
If the required division into teams doesn’t exist, print number -1. Otherwise, print lines. In each line print three integers xi, yi, zi (1 ≤ xi, yi, zi ≤ n) — the i-th team.
If there are multiple answers, you are allowed to print any of them.

Examples

Input-1
3 0
Output-1
3 2 1

Input-2
6 4
1 2
2 3
3 4
5 6
Output-2
-1

Input-3
3 3
1 2
2 3
1 3
Output-3
3 2 1

题目有点长,放在博客里,考虑到有些人连接外网可能比较费劲

题意
有个编程教练,要将他的n个学生分成三人一组(输入保证:n%3==0),给出m组条件,表示两两学生ai,bi必须分配到同一组。如果符合题意,输出分组结果(组内顺序与各组顺序随意),否则,输出-1.

某度翻译是真的坑,“split into groups of three” 翻译成“分成三组”,我当时就傻了,然后一看原题…
以后再也不能偷懒直接机翻了…

分析
从题意上应该可以很容易看出是考察并查集,用并查集的两个基本操作,合并+查找,顺带路径压缩,使n个学生形成明显的组别,保存于f[]数组中。

当你完成了这一步,这题的难点也就来了。

首先,题目要求三人一组输出,但进行了“并查集”操作后,每个队员只能知道自己的队长是谁,队员之间互相不了解,队长也不知道队员是谁,甚至不知道有几个队员,这给我们的输出工作带来了不便(你硬要说for循环多扫几遍,边扫边统计,那我也没办法),我个人更倾向于将各组从乱序中剔出,即用node结构体储存每一个组,如果一个人不属于任何组,那就自成一组。此番处理,便于输出,也便于接下来的组间合并。

其次,m组条件并不保证每组正好三人,有的队员可能不属于任何组,有些组可能2人,4人,甚至更多,多于3人可以直接判,输出-1,但是对于一人和两人的组,就要进行合并,如何合并,又成了我们眼前的一道坎。

合并,策略如下:优先将一人组与两人组合并(类似于贪心,更快形成三人组),当不存在两人组后,再进行一人组两两合并(其实这样又形成两人组),如此循环,直到所有节点都被访问过一次(用vis数组表示是否访问过),这里要注意,一人组投靠他人后,要将b[i].cnt计数器置0,否则将会产生重复访问问题。

合法性检测,若所有组成员皆为3,则合法,否则,不合题意。

我发现网上关于这题的题解的代码都比较长,有点点乱,所以写了这篇题解,希望能帮到大家

OK,扯远了。
"Talk is Cheap. Show me the Code"

#include<stdio.h>
#include<stdlib.h>
const int maxn = 55;
struct node {//小组
	int x[3];//成员
	int cnt = 0;
};
int f[maxn];//father
node b[maxn];//b[i]表示以i为父节点的小组
bool vis[maxn];//visit
int find(int t) {//寻找根节点
	return t == f[t] ? t : f[t] = find(f[t]);
}
int main(void)
{
	int n, m;
	scanf("%d %d", &n, &m);
	for (int i = 1; i <= n; i++)//初始化
		f[i] = i;
	while (m--)
	{
		int a, b;
		scanf("%d %d", &a, &b);
		vis[a] = vis[b] = true;
		a = find(a), b = find(b);
		f[a] = b;//合并
	}
	for (int i = 1; i <= n; i++) {
		int fa = find(i);
		b[fa].x[b[fa].cnt++] = i;//小组成员统计
	}
	for (int i = 1; i <= n; i++)//不足3人的小组合并,对于未提出要求的i队员,寻找1人或2人的小组与之合并
		if (!vis[i]) {
			bool flag = 0;
			for (int j = 1; j <= n; j++)
				if (i == j)//不能跟自己合并
					continue;
				else if (b[j].cnt == 2) {//2人小组
					b[j].x[b[j].cnt++] = i;
					flag = 1;
					b[i].cnt = b[i].x[0] = 0;
					vis[i] = vis[j] = true;
					break;
				}
			if (flag)continue;//优先补全2人的小组
			for(int j=1;j<=n;j++)
				if (i == j)//不能跟自己合并
					continue;
				else if (b[j].cnt == 1) {//1人小组
					b[j].x[b[j].cnt++] = i;
					b[i].cnt = b[i].x[0] = 0;
					vis[i] = vis[j] = true;
					break;
				}
		}
	int sum = 0;
	for (int i = 1; i <= n; i++)
		if (b[i].cnt == 3)//统计三人小组个数
			sum++;
	if (sum == n / 3) {//合法性检测
		for (int i = 1; i <= n; i++)
			if(b[i].cnt==3)
				printf("%d %d %d\n", b[i].x[0], b[i].x[1], b[i].x[2]);
	}
	else
		printf("-1\n");
	return 0;
}
发布了3 篇原创文章 · 获赞 0 · 访问量 57

猜你喜欢

转载自blog.csdn.net/qq_33374268/article/details/104415955