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