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