题意:
有n个点,m条边
将所有点分为三个点集,使每个点集内的点互不相交且与另两个点集中的每个点都有且仅有一条边相连
如果可行,则输出每个点所属的点集编号(1, 2, 3)
反之输出-1
Example
Input
6 11
1 2
1 3
1 4
1 5
1 6
2 4
2 5
2 6
3 4
3 5
3 6
Output
1 2 2 3 3 3
Input
4 6
1 2
1 3
1 4
2 3
2 4
3 4
Output
-1
Solution
- 根据性质可得:与1相连的点属于点集2、3
- 再在点集2、3中任选一个点遍历它的相邻点,若点在点集2、3中,则属于点集3
- 如此这般可以初分3个点集
- 接下来就是证明是否合法了:
- 不连通不合法,点集内有点相连不合法,点集内的点没有与另两个点集同时相连不合法
- 如此这般,轻松搞定
代码祭天!法力无边
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define il inline
#define re register
using namespace std;
typedef long long ll;
const int maxn = 3e5 + 10;
int n, m, cnt1, cnt2, cnt3;
vector<int> e[maxn];
int vised[maxn], vis[maxn];
//判断连通 \ 判断集合
int ans[maxn];//答案 => 所属集合
int edge[maxn];//记录边数
//int e1, e2, e3;//集合的边数
il void init() {
//e1 = e2 = e3 = 0;
cnt1 = cnt2 = cnt3 = 0;
memset(vised, 0, sizeof(vised));
memset(vis, 0, sizeof(vis));
}
il void dfs(int x) {
vised[x] = 1;
for(re int i = 0; i < e[x].size(); ++i) {
if(!vised[e[x][i]]) dfs(e[x][i]);
}
return ;
}
int main() {
init();
scanf("%d%d", &n, &m);
int u, v;
for(re int i = 1; i <= m; ++i) {
scanf("%d%d", &u, &v);
e[u].push_back(v); e[v].push_back(u);
edge[u]++; edge[v]++;
}
dfs(1);//不连通的图肯定不合法
for(re int i = 1; i <= n; ++i) {
if(!vised[i]) {
printf("-1\n");
return 0;
}
}
//ans[1] = 1;
for(re int i = 0; i < e[1].size(); ++i)
vis[e[1][i]] = 1;//从1遍历一遍,没标记的都是集合1
bool flag = 1;
for(re int i = 1; i <= n; ++i) {
if(!vis[i]) ans[i] = 1;
else if(vis[i] && flag) {
//处于2、3集合中,随机抽取一个点归为集合2
for(re int j = 0; j < e[i].size(); ++j) {
if(vis[e[i][j]]) ans[e[i][j]] = 3;
//被选的点相连且与1相连 => 属于集合3
}
flag = 0;
}
}
//验证过程:
for(re int i = 1; i <= n; ++i) {
if(ans[i] == 0) ans[i] = 2;
//剩下的点归为集合2
if(ans[i] == 1)
cnt1++;
if(ans[i] == 2)
cnt2++;
if(ans[i] == 3)
cnt3++;
}
//1.判断总边数
if(cnt1*cnt2 + cnt2*cnt3 + cnt3*cnt1 != m
|| cnt1 == 0 || cnt2 == 0 || cnt3 == 0) {
printf("-1\n");
return 0;
}
//2.判断集合中的点是否有连边
int s[5];//每个点连的两个集合边
for(re int i = 1; i <= n; ++i) {
memset(s, 0, sizeof(s));
for(re int j = 0; j < e[i].size(); ++j)
s[ans[e[i][j]]]++;//i所连的边所属的集合++
//统计完毕
if(s[ans[i]] != 0) {
printf("-1\n");
return 0;
}
if(ans[i] == 1) {
if(s[2] != cnt2 || s[3] != cnt3) {
printf("-1\n");
return 0;
}
}else if(ans[i] == 2) {
if(s[1] != cnt1 || s[3] != cnt3) {
printf("-1\n");
return 0;
}
}else {
if(s[1] != cnt1 || s[2] != cnt2) {
printf("-1\n");
return 0;
}
}
}
//3.判断一个集合的所有边数是否等于另两个几个的点数
for(re int i = 1; i <= n; ++i)
printf("%d ", ans[i]);
return 0;
}