[Educational Codeforces Round 87 (div2)]1354E - Graph Coloring[dp][01染色]

4 篇文章 0 订阅


参考博客

1354E - Graph Coloring[ d p dp dp][01染色]

题意

总共有 n n n 个点, m m m 条边
要使得任意边 ( u , v ) (u, v) (u,v) a b s ( c n t [ u ] − c n t [ v ] ) = 1 abs(cnt[u] - cnt[v]) = 1 abs(cnt[u]cnt[v])=1, c n t [ i ] cnt[i] cnt[i] 表示 i i i 点的编号
每个点的编号为 1 , 2 , 3 1, 2, 3 1,2,3 其中一个
保证边没有重边且没有自环
1 1 1 的个数为 n 1 n1 n1 2 2 2 的个数为 n 2 n2 n2 3 3 3 的个数为 n 3 n3 n3

做法

首先可以将图分为各各连通块
利用 d f s dfs dfs 找出所有的连通块,并进行染色编号
除去每个编号的数量的限制,可以发现
只有在环点数为奇数的时候才会无法满足任意边 ( u , v ) (u, v) (u,v) a b s ( c n t [ u ] − c n t [ v ] ) = 1 abs(cnt[u] - cnt[v]) = 1 abs(cnt[u]cnt[v])=1
因此先排除这样的情况,再继续之后的操作

接下来需要确定的是 2 2 2 的个数是否能满足
因为可以发现,只要 2 2 2 的位置确定了, 1 1 1 3 3 3 可以任意放置
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示 第 i i i 个连通块中 放 j j j 个编号为 2 2 2 的 是否可以
c n t [ i ] [ j ] cnt[i][j] cnt[i][j] 表示 第 i i i 个连通块中 编号为 j j j 的个数

转移方程为 ( k k k 为连通块编号)
2 2 2 1 1 1 的位置其实是可以调换的,即填 2 2 2 和 填 1 1 1 的位置反过来
因此个数也是可以调换的

for(int i = cnt[k][1]; i <= n2; ++i)
	dp[k][i] = dp[k][i] | dp[k-1][i-cnt[k-1][1]];
for(int i = cnt[k][2]; i <= n2; ++i)
	dp[k][i] = dp[k][i] | dp[k-1][i-cnt[k-1][2]];

最后输出的时候只要保证要填在 1 1 1,还是 2 2 2 的编号中, 2 2 2 的个数是可以取的
并且取完当前这个连通分量之后,接下来的连通分量也可以取即可

Code:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(a,b)    ((a)>(b)?(b):(a))
#define max(a,b)    ((a)>(b)?(a):(b))
const int inf = 0x3f3f3f3f;
const int maxm = 1e5 + 5;
const int maxn = 5e3 + 5;

struct node{
    int v, next;
}edge[maxm << 1];
int head[maxm << 1];
int cnt[maxn][maxn];  // i 连通  j 编号  cnt 个数
bool dp[maxn][maxn];  // i 连通  j 2的个数  dp 是否可以达到
int col[maxn];  // 点编号 1,2,3
int num[maxn];  // 连通编号
int cnt2[maxn]; // 结果
int tol, n, m, k;
int n1, n2, n3;
bool flag;

int re() {
    int x = 0, f = 1; char c = getchar();
    while(c < '0' || c > '9'){if(c=='-')f=-f; c=getchar();}
    while(c >= '0' && c <= '9'){x=(x<<3)+(x<<1)+c-'0'; c=getchar();}
    return x * f;
}

void add(int u, int v) {
    edge[tol] = node{v, head[u]}; head[u] = tol++;
    edge[tol] = node{u, head[v]}; head[v] = tol++;
}

inline void init() {
    tol = k = 0, flag = true;
    memset(dp, false, sizeof(dp));
    memset(cnt, 0, sizeof(cnt));
    memset(col, 0, sizeof(col));
    memset(head, -1, sizeof(head));
}

void dfs(int u, int pre, int x) {
    if(!flag)   return ;
    num[u] = k, col[u] = x, cnt[k][x] += 1;
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].v;
        if(pre == v)    continue;
        if(col[u] == col[v]) {  // 编号为一样的,不可以
            flag = false;
            return ;
        }else if(col[v] == 0) {
            dfs(v, u, 3 - x);
        }
    }
}

int main() {
    int u, v; init();
    n = re(), m = re();
    n1 = re(), n2 = re(), n3 = re();
    while(m--) {
        u = re(), v = re(); add(u, v);
    }

    dp[0][0] = true;
    for(int u = 1; u <= n; ++u) {
        if(col[u])  continue;
        k += 1;
        dfs(u, u, 1);
        if(!flag)   break;
        for(int i = cnt[k][1]; i <= n2; ++i) 
            dp[k][i] = dp[k][i] | dp[k - 1][i - cnt[k][1]];
        for(int i = cnt[k][2]; i <= n2; ++i)
            dp[k][i] = dp[k][i] | dp[k - 1][i - cnt[k][2]];
    }
    if(!flag || dp[k][n2] == false) {
        puts("NO");
        return 0;
    }

    while(k) {
        if(n2 >= cnt[k][1] && dp[k - 1][n2 - cnt[k][1]])
            cnt2[k] = 1, n2 -= cnt[k][1];
        else
            cnt2[k] = 2, n2 -= cnt[k][2];
        k -= 1;
    }
    puts("YES");
    for(int i = 1; i <= n; ++i) {
        if(col[i] == cnt2[num[i]])  printf("2");
        else if(n1-- > 0)           printf("1");
        else                        printf("3");
    }
    puts("");
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值