41. CF-Graph Coloring

链接

先考虑怎样的图可以染色或者不能染色。容易发现,1 和 3 不能相邻,它们其实是等价的。那么能染色的必要条件就是所有的子图都是二分图。此外,每个二分图都取左部或者右部之一,顶点数量之和需要正好等于 n 2 n_2 n2

于是这个问题转化成了普通的背包 dp,转移的时候记录一下就好了。

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
#define pb push_back
#define endl '\n'
using ll = long long;
using pii = pair<int, int>;
const int maxn = 5e3 + 5;

vector<int> g[maxn];
int vis[maxn], cnt, col[maxn];
vector<pii> color;
int dp[maxn][maxn];
bool ok;
void dfs(int u, int f, bool flag) {
    if (!ok) return;
    if (vis[u] == -1) {
        vis[u] = cnt;
        col[u] = flag;
    } else if (col[u] == flag) {
    	return;
    } else {
        ok = false;
        return;
    }
    if (!flag) color[cnt].first++;
    else color[cnt].second++;
    for (auto v : g[u]) {
        if (v == f) continue;
        dfs(v, u, !flag);
    }
}

void solve() {
    int n, m;
    cin >> n >> m;
    int n1, n2, n3;
    cin >> n1 >> n2 >> n3;
    for (int i = 1, u, v; i <= m; ++i) {
        cin >> u >> v;
        g[u].pb(v), g[v].pb(u);
    }
    memset(vis, -1, sizeof(vis));
    ok = true;
    color.pb({ 0, 0 });
    for (int i = 1; i <= n; ++i) {
        if (vis[i] == -1 && ok) {
        	cnt++;
            color.pb({ 0, 0 });
            dfs(i, 0, 0);
        }
    }
    if (!ok) {
        cout << "NO\n" << endl;
        return;
    }
    dp[0][0] = 1;
    for (int i = 1; i <= cnt; ++i) {
        for (int j = 0; j <= n; ++j) {
            auto [L, R] = color[i];
            if (j >= L && dp[i - 1][j - L]) {
                dp[i][j] = -1;
            }
            if (j >= R && dp[i - 1][j - R]) {
                dp[i][j] = 1;
            }
        }
    }
    if (!dp[cnt][n2]) {
        cout << "NO\n" << endl;
        return;
    }
    cout << "YES\n";
    vector<int> ans(n + 1);
    vector<int> chk(cnt + 1);
    int tot = n2;
    for (int i = cnt; i >= 1; --i) {
    	auto [L, R] = color[i];
    	if (dp[i][tot] == 1) {
    		tot -= R;
    		chk[i] = 1;
    	} else {
    		tot -= L;
    		chk[i] = 0;
    	}
    }
    for (int i = 1; i <= n; ++i) {
    	int cur = vis[i]; 
    	ans[i] = (chk[cur] == col[i]) + 1;
    	if (ans[i] == 1) {
    		if (n1) n1--;
    		else ans[i] = 3;
    	}
    }
    for (int i = 1; i <= n; ++i)
        cout << ans[i];
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int T = 1;
    // cin >> T;
    while (T--) {
        solve();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值