2021牛客暑期多校训练营8-C题

C题:Fuzzy Graph

题目大意

请添加图片描述

solution

对于基础目标,可以使用dfs遍历生成一颗树,将原图删去一些回边。然后将树上的节点蓝绿染色(染色是对称的,颜色可以随意选择),同时保证相邻节点颜色不同。
然后处理额外目标

  • 对额外目标1,当前蓝绿节点数都 ≥ n 3 \geq \frac{n}{3} 3n,则将一部分的蓝绿节点染成红色即可,同时保证无相邻颜色相同的节点。
  • 对额外目标2,若当前存在蓝色节点或绿色节点数量 < n 3 < \frac{n}{3} <3n即存在蓝色节点或绿色节点数量 > 2 n 3 > \frac{2n}{3} >32n ,简单推理可知,叶节点的数量 > n 3 >\frac{n}{3} >3n,则将所有的叶节点染成红色即可。推理如下:

不妨假设蓝色节点(B)数量 < n 3 <\frac{n}{3} <3n,绿色节点(G)数量 > 2 n 3 >\frac{2n}{3} >32n
B 叶 + B 非 叶 < n 3 G 叶 + G 非 叶 > 2 n 3 B_叶+B_{非叶}<\frac{n}{3} \\G_叶+G_{非叶}>\frac{2n}{3} B+B<3nG+G>32n
因为所有非叶节点都有至少一条边连向不同颜色的节点
G 非 叶 ≤ B 叶 + B 非 叶 < n 3 G_{非叶}\leq B_叶+B_{非叶}<\frac{n}{3} GB+B<3n
∴ G 叶 > n 3 \therefore G_叶>\frac{n}{3} G>3n
则叶节点的总数一定大于 n 3 \frac{n}{3} 3n

几个细节

  1. 处理额外目标1时要从叶节点开始染色,使后效性最小。
  2. 额外目标1染色时相邻节点不能是同色,可以开个数组标记。

code

#include<bits/stdc++.h>
using namespace std;
const int N=5e5+7;
int T,n,m;
char color[]={'B','G','R'};
int cl[N],leaf[N],sumB,sumG,sumR,vis[N],vis2[N];
vector <int> ve[N];
void dfs(int x,int clr)//生成树,并将初始颜色染上
{
	cl[x]=clr;
	if(cl[x]==0) sumB++;
	else sumG++;
	leaf[x]=1;
	vis[x]=1;
	for(int i=0;i<ve[x].size();i++)
	{
		if(vis[ve[x][i]]) continue;
		dfs(ve[x][i],clr^1);
		leaf[x]=0;
	}
}
void dfs2(int x,int fa,int g)
{
    vis[x]=1;
    bool flag=0;
    for(int i=0;i<ve[x].size();i++)//先遍历再修改,达到先染叶节点的效果
    {
    	int son=ve[x][i];
    	if(son==fa) continue;
    	if(vis[son]) continue;
		dfs2(son,x,g);
		if(vis2[son]) flag=1;
    }
    if(!flag){//相邻点不能同时染成红色
		if(cl[x]==0&&sumB>g){
			sumB--;
			vis2[x]=1;
			cl[x]=2;
		}
		else if(cl[x]==1&&sumG>g){
			sumG--;
			vis2[x]=1;
			cl[x]=2;
		}    	
    }
}
int main() 
{
	int u,v;
	scanf("%d",&T);
	while(T--)
	{
		sumB=sumG=sumR=0;
		scanf("%d%d",&n,&m);
		for(int i=0;i<=n;i++)
		{
			leaf[i]=vis2[i]=vis[i]=0;
			cl[i]=-1;
			ve[i].clear();
		}
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			ve[u].push_back(v);
			ve[v].push_back(u);
		}
		dfs(1,0);
		int g=n/3;
		if(sumB>2*g||sumG>2*g)//额外目标2,直接将叶节点染成红色
		{
			for(int i=1;i<=n;i++)
			if(leaf[i])
			cl[i]=2;
		}
		else{//额外目标1
			memset(vis,0,sizeof vis);
			dfs2(1,0,g);
		}
		for(int i=1;i<=n;i++)
		printf("%c",color[cl[i]]);
		printf("\n");
	}
	return 0;
}

小结

题目代码比较好写(虽然我debug了半天),想到构建Tarjan树,并通过贪心染色是解决题目的关键。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值