C-Fuzzy Graph
题目大意
链接:https://ac.nowcoder.com/acm/contest/11259/C
来源:牛客网
给你一个有n个点,m条边的无向图.现在有3种颜色——红®,绿(G),蓝(B).你需要给每个点涂上一种颜色,两个颜色相同的点之间的连边会变成这两个点的颜色.问怎么涂色才能即满足基础条件又至少满足另外两个条件中的一个.
基础条件:由没有涂过色的边构成的图仍是n个点的连通图.
另外两个条件:
1.不同颜色的点数量相同.
2.最多的同一颜色的点的邻边和该点颜色不同.
题目共有t组数据.
每组数据会先输入n(一定是三的倍数),m.
接下来m行每行两个整数a,b.表示点a,b之间有连边.
每组数据输出一行,输出每个点的涂色情况.
如果没有合法的答案输出CHEAT!.
思路
我们先用dfs来遍历图求出满足基本条件的图,每个相邻子节点和父节点赋上不同的颜色(先只用两种颜色),并记录它们出现的次数分别记为a,b.如果a,b中有一个大于了2*n/3.就大致是下图的情况.这时另外条件只有2才能满足.这时只要将所有叶节点都图上第三种颜色即可.
显然,这时三种颜色的点的数量必定不同,叶节点会很多.由于我们是用dfs建树,所以叶节点之间不会有连边.这时只需要将叶节点都涂上第三种颜色就行了.
其他情况,另外条件的第一个条件一定可行,因为保证了是3的倍数.所以这题不会输出CHEAT!.然后我们用dfs2来进行遍历,由于结合贪心思想,应该从叶节点开始修改.
并将父节点标记,无法修改.修改至a=b=n/3即可.
具体见代码.
代码实现
#include<iostream>
#include<cstdio>
#include<string.h>
#include<vector>
using namespace std;
int color[300010],co1=0,co2=0;
vector<int> ve[300010],leaf;
bool vis[300010],vis2[300010],co;
int n,m;
void dfs(int x,int step){
bool flag=0;
vis[x]=1;
if(step%2) {
color[x]=1;
co1++;
}
else{
color[x]=2;
co2++;
}
for(auto i:ve[x]){
if(!vis[i]){
flag=1;
dfs(i,step+1);
}
}
if(!flag) leaf.push_back(x);
}
int fv;
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(color[x]==1&&co1>g){
co1--;
vis2[x]=1;
color[x]=3;
}
else if(color[x]==2&&co2>g){
co2--;
vis2[x]=1;
color[x]=3;
}
}
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(vis2,0,sizeof(vis2));
string ans="";
leaf.clear();
co1=0,co2=0,co=0;
bool flag=0;
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)ve[i].clear();
while(m--){
int a,b;
scanf("%d%d",&a,&b);
ve[a].push_back(b);
ve[b].push_back(a);
}
dfs(1,0);
if(co1>2*n/3||co2>2*n/3){
for(auto i:leaf){
color[i]=3;
}
}
if(co1<=n*2/3&&co2<=n*2/3){
co=1;
memset(vis,0,sizeof(vis));
dfs2(1,0,n/3);
}
for(int i=1;i<=n;i++){
switch(color[i]){
case 1:{
ans+='R';
break;
}
case 2:{
ans+='G';
break;
}
case 3:{
ans+='B';
break;
}
}
}
cout<<ans<<endl;
}
}