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; }