二分图+dp——cf1354E

/*
先把点分联通块,做出二分图
二分图一侧染1,3,另一侧染2
分组背包求n2的可行方案 
*/
#include<bits/stdc++.h>
using namespace std;
#define N 5005
#define M 100005
 
int n,m,n1,n2,n3; 
vector<int>G[N];
 
int vis[N],cnt,flag;
vector<int>s[N][3];
void dfs(int u,int c){
    vis[u]=c;
    s[cnt][c].push_back(u);
    
    int cc;
    if(c==1)cc=2;else cc=1;    
    for(auto v:G[u]){
        if(!vis[v])dfs(v,cc);
        else if(vis[v]==vis[u]){
            flag=1;
            return;
        }
    }
}
 
int useless[N][N],dp[N][N],ans[N];
 
int main(){
    cin>>n>>m;cin>>n1>>n2>>n3;
    for(int i=1;i<=m;i++){
        int u,v;scanf("%d%d",&u,&v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(int i=1;i<=n;i++)if(!vis[i]){
        ++cnt;
        dfs(i,1);
        if(flag){puts("NO");return 0;}
    }
    
    dp[0][0]=1;
    for(int i=1;i<=cnt;i++){
        for(int j=s[i][1].size();j<=n2;j++)
            dp[i][j]|=dp[i-1][j-s[i][1].size()];
        for(int j=s[i][2].size();j<=n2;j++)
            dp[i][j]|=dp[i-1][j-s[i][2].size()];
    }
    if(!dp[cnt][n2]){puts("NO");return 0;}
    int now=n2,t=cnt;
    while(t){
        if(now>=s[t][1].size() && dp[t-1][now-s[t][1].size()]){
            now-=s[t][1].size();
            for(auto x:s[t][1])ans[x]=2;
            s[t][1].clear();
        }
        else if(now>=s[t][2].size() && dp[t-1][now-s[t][2].size()]){
            now-=s[t][2].size();
            for(auto x:s[t][2])ans[x]=2;
            s[t][2].clear();
        }
        t--;
    }
    if(now){
        puts("NO");return 0;
    }
    for(int i=1;i<=cnt;i++){
        if(s[i][1].size()){
            for(auto x:s[i][1])
                if(n1)ans[x]=1,n1--;
                else ans[x]=3;
        }else {
            for(auto x:s[i][2])
                if(n1)ans[x]=1,n1--;
                else ans[x]=3;
        }
    }
    puts("YES");
    for(int i=1;i<=n;i++)cout<<ans[i];
    
}
原文地址:https://www.cnblogs.com/zsben991126/p/12918713.html