E. Graph Coloring(二分图+分组背包)
思路:将每个二分子图看作一组,两部分的结点看成两个物品,首先判断每个子图是不是都为二分图,若不是则无解,因为1和3等价,所以只需要对 和2进行染色判断,然后再用分组背包,看能否恰好装下体积为 的物品。
因为要输出分案,所以需要一个数组来进行回溯记录路径。回溯的时候判断一下前一个路径点的物品数是来自于1,还是来自于2即可。
时间复杂度
AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=5e3+10,M=1e5+5;
#define mst(a) memset(a,0,sizeof a)
struct edge{
int to,nt;
}e[M<<1];
int col[N],n,m,f[N][N],cnt[N][3],bl[N],id,tot=1,h[N],n1,n2,n3,path[N];//id表示二分子图编号,bl[i]表示结点i属于那个二分子图
void add(int u,int v){//col[i]结点i的颜色 f[i][j]第i组染色后有j个n2被染色.
e[tot]={v,h[u]};
h[u]=tot++;
}
void dfs(int u,int c){ //染色法判断二分图
col[u]=c,bl[u]=id,cnt[id][c]++;
for(int i=h[u];i;i=e[i].nt){
int v=e[i].to;
if(col[v]==col[u]){
puts("NO");
exit(0);
}
if(!col[v]) dfs(v,3-c);
}
}
int main(){
scanf("%d%d%d%d%d",&n,&m,&n1,&n2,&n3);
for(int i=1,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
f[0][0]=1;//初始化
for(int i=1;i<=n;i++){
if(col[i]) continue;
id++;
dfs(i,1);
for(int j=cnt[id][1];j<=n2;j++) //分组背包dp
f[id][j]|=f[id-1][j-cnt[id][1]];
for(int j=cnt[id][2];j<=n2;j++)
f[id][j]|=f[id-1][j-cnt[id][2]];
}
if(!f[id][n2]){
puts("NO");
return 0;
}
puts("YES");
while(id){ //回溯记录路径
path[id]=f[id-1][n2-cnt[id][1]];
if(path[id]) n2-=cnt[id][1];
else n2-=cnt[id][2];
id--;
}
for(int i=1;i<=n;i++){
if(path[bl[i]]) col[i]=3-col[i];
if(col[i]==2) printf("2");
else if(n1>0) printf("1"),n1--;
else printf("3");
}
return 0;
}