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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章