CF1354E Graph Coloring(构造二分图+背包)
Posted zhanglichen
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1354E Graph Coloring(构造二分图+背包)相关的知识,希望对你有一定的参考价值。
看大神的代码一脸懵,学了很多新东西,背包理解的太浅了,二分图染色不太会。
/* * cf1354E * 题意: * 给出一个无向连通图,和n1,n2,n3分别表示需要染色1,2,3的节点数量。 * 图不保证连通,并且需要保证一条边的两个节点的色号之差的绝对值为1。 * 请你计算是否存在合理的方案,并输出其中任意一种。 * 题解: * 问题可以转化为,必须给一批点染2号色,给1批点染13号色,且这两批点内部没有边,两批点之间有边。 * 可以转化成一个二分图的构造问题。 * 首先,我们对每个连通块构造二分图,这里用12标记二分图里的两种节点。 * 发现奇环,说明当前连通块无法构造二分图,那么就不可能合法,直接退出。 * 然后我们可以推导出每个连通块中1号点的数量和2号点的数量。 * 开一个二维dp数组,表示标记i个连通块数量,j个标记为2的情况下是否存在合法 * 所有连通块可以转化成背包,连通块里的两种节点数量就是背包的重量。 * 可以进一步推导出状态转移方程,如果终点状态不合法直接退出。 * 然后从终点倒推,如果当前状态不合法,说明需要取反,就反向标记。 * 标记的时候采用这种方法: * 只记录当前点是2颜色还是1颜色,如果是2颜色就直接染,如果是1颜色,先看一下n1还有没有剩余,如果有染1,如果没有染2。 */ #include<bits/stdc++.h> using namespace std; const int maxn=5005; vector<int> g[maxn]; int n,m,n1,n2,n3; int p[maxn]; int c[maxn]; int cnt; int d1[maxn];//每个连通块中1号点的数量 int d2[maxn];//每个连通块中2号点的数量 bool dp[maxn][maxn];//标记i个连通块数量,j个标记为2的情况下是否存在合法 bool rev[maxn];//标记该连通块是否反向染色 void dfs (int u) { if (c[u]==1) d1[cnt]++;//如果当前是1号点 else d2[cnt]++;//如果当前是2号点 for (int v:g[u]) { if (!c[v]) { c[v]=3-c[u];//用1,2染色处理二分图 p[v]=cnt;//标记v所在连通块 dfs(v); } else if (c[u]==c[v]) { printf("NO ");//如果发现染色一样,说明发现奇环,不合法 exit(0); } } } int main () { cin>>n>>m>>n1>>n2>>n3; for (int i=0;i<m;i++) { int x,y; cin>>x>>y; g[x].push_back(y); g[y].push_back(x); } cnt=0; dp[0][0]=1;//0个连通块,0个点被染色成2肯定合法 for (int i=1;i<=n;i++) { if (!c[i]) { p[i]=cnt;//i号点所属的连通块编号是cnt c[i]=1;//给i号点染1颜色 dfs(i);//处理二分图 for (int j=d1[cnt];j<=n2;j++) dp[cnt+1][j]+=dp[cnt][j-d1[cnt]];//如果在已有的连通块数量下标记j-d1[cnt]数量的点为2的情况合法,那么dp[cnt+1][j]就一定合法 for (int j=d2[cnt];j<=n2;j++) dp[cnt+1][j]+=dp[cnt][j-d2[cnt]];//同理 cnt++; } } if (!dp[cnt][n2]) { //如果cnt个连通块,n2个点的情况没有合法的,输出NO。 printf("NO "); return 0; } printf("YES "); while (cnt--) { rev[cnt]=!dp[cnt][n2-d2[cnt]];//如果只剩cnt个连通块,同时要给n2-d2[cnt]数量的点染2号颜色不存在合法的方案,说明需要取反 if (rev[cnt]) n2-=d1[cnt];//如果需要取反,说明当前给1号点染2号色 else n2-=d2[cnt];//不需要取反,说明当前给2号点染2号色 } for (int i=1;i<=n;i++) { if (rev[p[i]]) c[i]=3-c[i];//如果取反,直接把染色情况置为反色(这里只有2种情况,2号色和1号色) if (c[i]==2) printf("2"); else if (n1) { printf("1");//涂1号色的时候看一下n1有没有用完,如果用完了就改涂3号色 n1--; } else printf("3"); } }
以上是关于CF1354E Graph Coloring(构造二分图+背包)的主要内容,如果未能解决你的问题,请参考以下文章
#0011.「codeforces」1354E Graph Coloring
Codeforces 1354E(Graph Coloring,二分图+dp)
CF662B Graph Coloring题解--zhengjun