http://codeforces.com/gym/100345/attachments
-E - New Mayors
题意:
给一无向图和三种颜色,顶点n(n<=500),边m,
告诉你这个图里,对于任意节点U, 定义N【u】为u的所有直接儿子节点的集合,并且集合中任意两个点可以通过N【u】中的点互相到达(不经过U)。
现在让你用三种颜色染图,规定一条边两端颜色不可相等。
如果是直接用三个颜色去染图 复杂度感人。。。
由于题目的图有一个性质【即u的所有直接儿子节点是联通的】(据此可以知道儿子们的二分图染色方案唯一)
那么我们只看u以及 u的直接儿子:
首先u和所有儿子相连,必然【U占一种颜色,所有儿子共用2种颜色】,如果儿子们用了三种颜色,那么U必然会和一个儿子冲突。 因此,我们把U拔出来,【作为root】,把U的儿子们构成的图看作一个新的子图,用这个图去跑二分图染色 ,如果染色失败,则不存在合法方案,否则 U及其儿子们的染色方案目前是合法的。
【然后从U的每个作为新的root,去染色】
二分图染色部分我们用一个dfs做
而不断选出新的root来去染色这部分我们可以用一个队列,每次把root的儿子都放进队列,然后不断从队列取出root,当队列空则 另找一个新的root放进队列【整个图可能非联通】
注意“【每次跑二分图染色的节点只有U的直接儿子节点】
ac代码:
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
const double pi=acos(-1.0);
double eps=0.000001;
int min(int a,int b)
{return a<b?a:b;}
int max(int a,int b)
{return a>b?a:b;}
vector<int> mp[600]; //邻接矩阵
int expand[600]; //表示是否进入过队列
int col[600]; //节点颜色
int vaild[600]; //表示参与二分图染色的节点
int vis[600]; //标记每次跑二分图染色时该节点是否访问过
int dfs(int x,int rt)
{
int i;
for (i=0;i<mp[x].size();i++)
{
int v=mp[x][i];
if (!vaild[v])continue;
if (col[v]&&col[v]!=6-col[rt]-col[x] ) return 0; //涂过色且颜色与当前方案冲突
if (!vis[v]) //如果在本次染色过程未访问过,则dfs进入
{
vis[v]=1;
if (!col[v])
col[v]=6-col[rt]-col[x] ; //无色则染色
if (dfs(v,rt)==0) return 0; //递归进入
}
}
return 1;
}
queue<int >q; //作为染色root节点
int main()
{
freopen( "mayors.in","r",stdin ); // scanf 从1.txt输入
freopen( "mayors.out","w",stdout ); //printf输出到1.tx
int n,m;
cin>>n>>m;
int x,y,i;
for (i=1;i<=n;i++) mp[i].clear();
for (i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
mp[x].push_back(y);
mp[y].push_back(x);
}
int flag=0;
int idx=1;
while (!q.empty()||idx<=n)
{
if (q.empty()) //队列已无可扩节点,寻找新的root
for (;idx<=n;idx++)
{
if (expand[idx]) continue;//如果已进过队列,则忽略
q.push(idx);
col[idx]=1; //颜色涂1
expand[idx]=1; //表示进过队列
break;
}
if (q.empty())break;
int rt=q.front();q.pop();
// printf("%d\n",q.size());
if (mp[rt].size()==0) continue;
int start=mp[rt][0]; //任取一个start节点
memset(vaild,0,sizeof (vaild));
memset(vis,0,sizeof (vis));
for (i=0;i<mp[rt].size();i++)
{
int v=mp[rt][i];
vaild[v]=1; //表示该节点参与本次二分图染色
if (!expand[v]) //加入队列
q.push(v);
expand[v]=1;
if (col[v]) start=v; //如果该节点颜色确定,则从它出发染色
}
if (!col[start]) col[start]=2; //如果全部节点无颜色,则任意涂个2或3
vis[start]=1;
if (!dfs(start,rt))
{ flag=1;break;} //染色失败,不存在合法方案
}
if (flag) {printf("Plan failed\n");return 0;}
printf("Plan OK\n");
for (i=1;i<=n;i++)
{
if (col[i]==1)
printf("G");
else if (col[i]==2)
printf("B");
else printf("R");
}
printf("\n");
return 0;
}

本文介绍了如何使用二分图染色算法解决Andrew Stankevich Contest中关于无向图染色的问题。针对给定的条件,通过分析图的性质,将节点分为根节点和子节点,并利用DFS进行二分图染色。在染色过程中,通过维护一个队列来寻找新的根节点,确保所有子节点的染色方案合法。若染色失败,说明不存在满足条件的染色方案。

1239

被折叠的 条评论
为什么被折叠?



