Codeforces 1228D. Complete Tripartite

距离比赛结束还有三分钟过题,真是刺激

题意:

给你一个无向简单图,可能不连通,没有自环,没有重边。
问你能否将所有点划分成三个集合,每个集合中的所有点没有边直接连接,而且与其他两个集合中的所有点都有边直接连接(思考一下,可以发现必为连通图)。
如果能够构造出,输入任意一个

分析与解答

我们考虑点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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值