距离比赛结束还有三分钟过题,真是刺激
题意:
给你一个无向简单图,可能不连通,没有自环,没有重边。
问你能否将所有点划分成三个集合,每个集合中的所有点没有边直接连接,而且与其他两个集合中的所有点都有边直接连接(思考一下,可以发现必为连通图)。
如果能够构造出,输入任意一个
分析与解答
我们考虑点1,假设它属于集合1,那么它必须与集合2与集合3中的所有点有边直接连接,那么所有与它没有直接边连接的点一定和它属于同一个集合。
因此我们用如下方式构造:
① 找出所有与1连接的点,这些点与1构成了集合1
② 找出一个与1连接的点,记为 v 2 v_2 v2,所有与它连接的,而且不在集合1中的点构成了集合2
③ 即不在集合2也不在集合3中的点构成集合3
判断方法为:
① 对于每一个点记录所有与他直接连接的点,统计它与其他的集合的连接点的数量
② 对于集合1中的点,要满足到直接与集合2、集合3连接的点的数量分别为集合2、集合3的元素个数,同时与集合1中直接连接的点的数量为0
③ 对于每个点进行上述的判断,如果有任何一个点不满足,则为-1;特别的,如果有任何一个集合为空集,为-1
代码
/*************************************************************************
> File Name: 2019_9_29_4.cpp
> Author: z472421519
> Mail:
> Created Time: 2019年09月29日 星期日 22时19分04秒
************************************************************************/
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#define MAXN 100013
using namespace std;
int edgesize;
int id[MAXN],g[MAXN],res[MAXN][5];
bool vis[MAXN];
struct edge
{
int f,t;
void var(int f1,int t1)
{
f = f1;
t = t1;
}
}edges[MAXN * 6];
void addedge(int f,int t)
{
edges[++edgesize].var(g[f],t);
g[f] = edgesize;
}
int main()
{
int n,m,u,v;
scanf("%d%d",&n,&m);
//初始化
memset(id,0,sizeof(id));//id[i]代表点i所属集合
memset(g,-1,sizeof(g));
memset(res,0,sizeof(res));//res[i][k]代表点i与集合k中的点直接连接的点的个数
edgesize = 0;
int cnt1,cnt2,cnt3;//集合中元素的个数
cnt1 = cnt2 = cnt3 = 0;
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v);
addedge(v,u);
}
//printf("T\n");
//id[1] = 1;
memset(vis,false,sizeof(vis));
//去掉所有与1直接连接的点
for(int i = g[1];i != -1;i = edges[i].f)
{
vis[edges[i].t] = true;
//printf("%d\n",i);
}
//printf("T\n");
for(int i = 1;i <= n;i++)
if(!vis[i])
{
id[i] = 1;
cnt1++;
}
//printf("T\n");
//找到一个与1直接连接的点,重复之前的操作
memset(vis,false,sizeof(vis));
//printf("%d\n",g[edges[g[1]].t]);
for(int i = g[edges[g[1]].t];i != -1;i = edges[i].f)
{
vis[edges[i].t] = true;
}
//id[edges[g[1]].t] = 2;
for(int j = 1;j <= n;j++)
if(!vis[j] && ! id[j])
{
id[j] = 2;
cnt2++;
}
//剩余的点为集合3中的点
for(int i = 1;i <= n;i++)
if(!id[i])
{
id[i] = 3;
cnt3++;
}
//printf("T%d %d %d \n",cnt1,cnt2,cnt3);
//统计所有的点与各个集合的点的连接情况
for(int i = 1;i <= n;i++)
for(int j = g[i];j != -1;j = edges[j].f)
res[i][id[edges[j].t]]++;
#if 0
for(int i = 1;i <= n;i++)
printf("%d %d %d %d\n",id[i],res[i][1],res[i][2],res[i][3]);
#endif
//判断部分
for(int i = 1;i <= n;i++)
{
if(id[i] == 1)
{
if(res[i][2] < cnt2 || res[i][3] < cnt3 || res[i][1])
{
printf("-1\n");
return 0;
}
}
if(id[i] == 2)
{
if(res[i][1] < cnt1 || res[i][3] < cnt3 || res[i][2])
{
printf("-1\n");
return 0;
}
}
if(id[i] == 3)
{
if(res[i][2] < cnt2 || res[i][1] < cnt1 || res[i][3])
{
printf("-1\n");
return 0;
}
}
}
if(cnt1 == 0 || cnt2 == 0 || cnt3 == 0)
{
printf("-1\n");
return 0;
}
for(int i = 1;i <= n;i++)
printf("%d ",id[i]);
return 0;
}