E. Graph Coloring(二分图+分组背包)

E. Graph Coloring(二分图+分组背包)

传送门

思路:将每个二分子图看作一组,两部分的结点看成两个物品,首先判断每个子图是不是都为二分图,若不是则无解,因为1和3等价,所以只需要对 1 1 和2进行染色判断,然后再用分组背包,看能否恰好装下体积为 n 2 n_2 的物品。

因为要输出分案,所以需要一个数组来进行回溯记录路径。回溯的时候判断一下前一个路径点的物品数是来自于1,还是来自于2即可。

时间复杂度 O ( n 2 ) O(n^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;
} 

猜你喜欢

转载自blog.csdn.net/weixin_45750972/article/details/106237797