CodeForces_1354E Graph Coloring(DFS+动态规划)

Graph Coloring

time limit per test:2 seconds
memory limit per test:256 megabytes
Problem Description

You are given an undirected graph without self-loops or multiple edges which consists of n vertices and m edges. Also you are given three integers n 1 n_1 n1, n 2 n_2 n2 and n 3 n_3 n3.

Can you label each vertex with one of three numbers 1, 2 or 3 in such way, that:

  • 1.Each vertex should be labeled by exactly one number 1, 2 or 3;
  • 2.The total number of vertices with label 1 should be equal to n 1 n_1 n1;
  • 3.The total number of vertices with label 2 should be equal to n 2 n_2 n2;
  • 4.The total number of vertices with label 3 should be equal to n 3 n_3 n3;
  • 5. ∣ c o l u − c o l v ∣ = 1 |col_u−col_v|=1 colucolv=1 for each edge (u,v), where c o l x col_x colx is the label of vertex x.

If there are multiple valid labelings, print any of them.

Output

If valid labeling exists then print “YES” (without quotes) in the first line. In the second line print string of length n consisting of 1, 2 and 3. The i i i-th letter should be equal to the label of the i i i-th vertex.

If there is no valid labeling, print “NO” (without quotes).

Sample Input

6 3
2 2 2
3 1
5 4
2 5

Sample Output

YES
112323

题意

n n n个点 m m m条边的无向图,要求给n个点赋值,每个点可以赋为1、2、3中的一个,n个点共有 n 1 n_1 n1个值为1的点, n 2 n_2 n2个值为2的点, n 3 n_3 n3个值为3的点。且每条边的两个顶点的值满足 ∣ c o l u − c o l v ∣ = 1 |col_u−col_v|=1 colucolv=1。求是否存在合适的赋值方案。

题解

每条边的两个顶点的值满足 ∣ c o l u − c o l v ∣ = 1 |col_u−col_v|=1 colucolv=1,所以赋值为2的点很重要,而1,3实际上
并无区别(1,3之间不会有边,且都只与2连边)。实际上就是求能否变为一个二分图,且二分图的一个集合的大小为 n 2 n_2 n2
二分图:DFS判断是否存在奇环即可。在DFS的同时,对于每个连通块,求出两个集合的大小,分别计做 s z 1 sz1 sz1, s z 2 sz2 sz2

赋值为2的点:在DFS后,共有 c n t cnt cnt个连通块,第 i i i连通块二分图两边集合的大小为 s z 1 i sz1_i sz1i s z 2 i sz2_i sz2i。动态规划,求 s z 1 sz1 sz1 s z 2 sz2 sz2的组合能否存在方案使和为 n 2 n_2 n2即可。

若存在奇环或不存在方案使和为 n 2 n_2 n2,则无解。否则利用动态规划的记录,优先标记赋值为2的点,剩下的点赋值为1或3即可。

#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<map>
#include<vector>
#include<queue>
#include<iterator>
#define dbg(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
#define eps 1e-6
 
using namespace std;
typedef long long LL;   
typedef pair<int, int> P;
const int maxn = 5020;
const double pi = acos(-1.0);
const int mod = 1000000007;
int cnt, sig, col[maxn], dp[maxn][maxn];
char ans[maxn];
vector<int> g[maxn], po[maxn][4];
void dfs(int u, int f, int cl);

int main()
{
    int n, m, i, j, k, n1, n2, n3;
    sig = 1;
    memset(col, -1, sizeof(col));
    scanf("%d %d %d %d %d", &n, &m, &n1, &n2, &n3);
    for(i=0;i<m;i++){
        scanf("%d %d", &j, &k);
        g[j].push_back(k);
        g[k].push_back(j);
    }
    cnt = 0;
    for(i=1;i<=n;i++)
        if(col[i] == -1){
            dfs(i, 0, 1);
            cnt++;
        }
    if(!sig)printf("NO\n");
    else{
        dp[0][0] = 1;
        for(i=0;i<cnt;i++){
            int a1 = po[i][0].size(), a2 = po[i][1].size();
            for(j=0;j<=n2;j++)
            if(dp[i][j])
            {
                if(j+a1<=n2)dp[i+1][j+a1] = 1;
                if(j+a2<=n2)dp[i+1][j+a2] = 1;
            }
        }
        if(dp[cnt][n2] == 0)printf("NO\n");
        else{
            k = n2;
            for(i=cnt;i>0;i--){
                int v;
                if(dp[i-1][k-po[i-1][0].size()])
                    v = 0;
                else v = 1;
                k -= po[i-1][v].size();
                for(j=0;j<po[i-1][v].size();j++)
                    ans[po[i-1][v][j]] = '2';
            }
            for(i=1;i<=n;i++){
                if(ans[i] != '2'){
                    if(n1)ans[i] = '1', n1--;
                    else ans[i] = '3', n3--;
                }
            }
            printf("YES\n%s\n", ans+1);
        }
    }
    return 0;
}

void dfs(int u, int f, int cl)
{
    col[u] = cl;   
    po[cnt][cl].push_back(u);
    for(int i=0;i<g[u].size();i++)
        if(g[u][i] != f){
            int v = g[u][i];
            if(col[v] == -1)dfs(v, u, !cl);
            else if(col[v] == col[u])sig = 0;
        }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值