标签:nbsp amp 个数 lock rap tip node coloring tle
You are given an undirected graph without self-loops or multiple edges which consists of nn vertices and mm edges. Also you are given three integers n1n1 , n2n2 and n3n3 .
Can you label each vertex with one of three numbers 1, 2 or 3 in such way, that:
If there are multiple valid labelings, print any of them.
The first line contains two integers nn and mm (1≤n≤50001≤n≤5000 ; 0≤m≤1050≤m≤105 ) — the number of vertices and edges in the graph.
The second line contains three integers n1n1 , n2n2 and n3n3 (0≤n1,n2,n3≤n0≤n1,n2,n3≤n ) — the number of labels 1, 2 and 3, respectively. It‘s guaranteed that n1+n2+n3=nn1+n2+n3=n .
Next mm lines contan description of edges: the ii -th line contains two integers uiui , vivi (1≤ui,vi≤n1≤ui,vi≤n ; ui≠viui≠vi ) — the vertices the ii -th edge connects. It‘s guaranteed that the graph doesn‘t contain self-loops or multiple edges.
If valid labeling exists then print "YES" (without quotes) in the first line. In the second line print string of length nn consisting of 1, 2 and 3. The ii -th letter should be equal to the label of the ii -th vertex.
If there is no valid labeling, print "NO" (without quotes).
6 3 2 2 2 3 1 5 4 2 5
YES 112323
5 9 0 2 3 1 2 1 3 1 5 2 3 2 4 2 5 3 4 3 5 4 5
NO
题目大意:给一张图(可能不连通)每个点可以涂1或2或3,要满足相邻点的值的差的绝对值等于1,可行的话输出方案。
貌似没有很多这个题的博客…就说一下我的思路,不过感觉并不好而且代码不好写
首先我们能注意到这样一个事实:1和3实际上是没有区别的,因为1和3都只能连2,而2可以连接1或者3,那么我们就可以把1和3看作一类,把2看作另一类。
然后就有了这个结论:对于一个连通图来说,如果其存在奇环,那么一定是NO的(不理解可以画一个七边形,从一个点出发交替填0和1模拟一下)。
这样一来就好办了,对于输入数据建无向图(记得数组开两倍大小),进行一次或多次DFS(取决于有几个连通块),把每个连通块的点分成两大类(分别标记为0和1)。
至于从哪个点开始,哪个点标记成0哪个点标记成1是没有关系的,只要满足每条边两端点的标记不相同即可。
然后整个问题转化成一个DP问题:对于每个连通块我们可以选择标记为0的点来填上2也可以选择标记为1的点来填上2,
那么只要所有的连通块填上2的点的数目等于n2就是YES(剩下的随便填1或者3),这实际上是判断可行性,如果可行的话还需要记录路径。
不妨设DP[i][j]表示从1到第i个结构体能否选出j个点涂成2。最终如果DP[cnt][n2]为真则是YES的(cnt表示连通块的数目)。
转移方程: DP[i][j] |= (DP[i-1][j-b[i].zero] | DP[i-1][j-b[i].one]) 其中b[i].zero表示第i个连通块里标记为0的点的个数,one同理.
但这样的DP数组是不能满足我们的要求的,因为无法记录路径,这其实好办,只需要把DP数组的int类型换成自定义的结构体类型。
结构体不仅存储布尔值,还要存储如果布尔值为true的话是从上一个阶段哪个位置转移过来的,这样只需要从DP[cnt][n2]沿着记录的路径回溯回去就行了(因此也不好把数组压缩成一维的了),
回溯时,对于某个连通块里的点按照DP时为true的状态把相应的字符串的位置直接填上2,整个填完后再从头找字符串为空的地方随便填1和3即可。
#include <bits/stdc++.h> #define N 5005 #define M 200005//要开两倍大小存反向边 using namespace std; int n,m,n1,n2,n3,tot=0,head[N],ver[M],Next[M]; int cnt=0;//belong[i]为i所属的连通块编号 bool mark[N];//mark[i]为1 or 0代表i在所处的连通块里涂哪一类颜色 vector<vector<int> >v; //确定每个连通块对应的节点 bool flag=1,belong[N]={0};//belong[i]表示这个点是否被访问过 char s[5005];//存储答案 struct block { int zero;//连通块里标记为0的点的个数 int one; }b[N]; struct node { bool ok;//判断可行性 //y 表示从上一层哪个位置转移过来的 num表示这个连通块标记为num的点应该涂成2 bool num; int y; }**dp;//dp[i][j]表示 void add(int x,int y) { ver[++tot]=y,Next[tot]=head[x],head[x]=tot; } void dfs(int x,int pre,int num,bool color)//此处的color只有0和1两类(0的话要么涂2要么涂1 3) { int i; mark[x]=color; if(color==0) b[num].zero++;//统计每个连通块里0的数量 else b[num].one++; color=1-color;//下一层要换过来 belong[x]=1;//访问过 v[num].push_back(x);//对于每个连通块把点给塞进去 for(i=head[x];i;i=Next[i]) { int y=ver[i]; if(y==pre)continue; if(!belong[y]) { dfs(y,x,num,color); } else { if(mark[y]==mark[x])flag=0;//00碰头或者11碰头 } } } void draw(int nx,int ny)//涂色 { int i; for(i=0;i<v[nx].size();i++) { if(dp[nx][ny].num==0)//如果选择把这个连通块标记为0的点涂上2 { if(mark[v[nx][i]]==0) s[v[nx][i]]=‘2‘; } else if(dp[nx][ny].num==1) { if(mark[v[nx][i]]==1) s[v[nx][i]]=‘2‘; } } if(nx==1)return;//边界 else draw(nx-1,dp[nx][ny].y); } int main() { cin>>n>>m; cin>>n1>>n2>>n3; int i,j; for(i=1;i<=n;i++) { b[i].zero=b[i].one=0; } for(i=1;i<=m;i++) { int x,y; cin>>x>>y; add(x,y); add(y,x); } if(m==0)//没有边全是点 一定可以 { for(i=1;i<=n;i++) { if(n1)s[i]=‘1‘,n1--; else if(n2) s[i]=‘2‘,n2--; else s[i]=‘3‘,n3--; } cout<<"YES"<<endl; for(i=1;i<=n;i++) { cout<<s[i]; } return 0; } else { if(!n2)//有边且n2=0 一定不可以 { cout<<"NO"; return 0; } else if(n1==0&&n2==0 || n2==0&&n3==0 || n1==0&&n3==0)//有边且两个数为0 一定不可以 { cout<<"NO"; return 0; } } vector<int> temp; v.push_back(temp);//凑数的 for(i=1;i<=n;i++) { if(!belong[i]) { vector<int> temp; v.push_back(temp); cnt++;//连通块++ dfs(i,0,cnt,0); if(!flag) { cout<<"NO"; return 0; } } } dp = new node *[cnt+4];//懒得改了 之前莫名其妙炸空间 其实不这么写 for (i = 0; i <= cnt; i++) { dp[i] = new node [n+4]; } // for(i=0;i<=cnt;i++) { for(j=0;j<=n;j++)dp[i][j].ok=0; } dp[1][b[1].zero].ok=1;//初始化 dp[1][b[1].zero].num=0; dp[1][b[1].one].ok=1; dp[1][b[1].one].num=1; for(i=2;i<=cnt;i++) { for(j=b[i].zero;j<=n;j++) { if(dp[i-1][j-b[i].zero].ok) { dp[i][j].ok=1; dp[i][j].y=j-b[i].zero;//从哪转移过来 dp[i][j].num=0;//i这个连通块的所有标记为0的点填2 } } //最好合起来写 但确定j的起始值写起来比较啰嗦 for(j=b[i].one;j<=n;j++) { if(dp[i-1][j-b[i].one].ok) { dp[i][j].ok=1; dp[i][j].y=j-b[i].one; dp[i][j].num=1;//i这个连通块的所有1的位置填2 } } } if(dp[cnt][n2].ok)//能凑出来n2 { draw(cnt,n2);//开始涂色 } else { cout<<"NO"; return 0; } for(i=1;i<=n;i++)//涂剩下的1和3 { if(s[i]!=‘2‘) { if(n1)s[i]=‘1‘,n1--; else s[i]=‘3‘,n3--; } } cout<<"YES"<<endl; for(i=1;i<=n;i++)cout<<s[i]; }
Educational Codeforces Round 87 (Rated for Div. 2) E. Graph Coloring (DFS+DP+状态记录+构造)
标签:nbsp amp 个数 lock rap tip node coloring tle
原文地址:https://www.cnblogs.com/lipoicyclic/p/12913950.html