题目链接
题目大意:
给一个图(不保证连通),需要用1,2,3对图上的每个顶点进行标号,使得标号完成之后相邻顶点的号码的差的绝对值为1,其中n1,n2,n3分别为标号为1,2,3的数量,问这种标号方法是否存在,如果存在则输出标号方案。
解题思路:
要使相邻之差绝对值为1,则相邻标号只能为1-2,2-3这两种方案,可以发现1和3的地位相同,所以可以把题目转化为对二分图染色,只要分奇偶染色就行了。
(1)对每个连通块进行dfs,检查是否为二分图,如果不是则直接输出“NO”,顺便记录每个连通块的奇数点个数O[i],和偶数点个数Z[i]。
(2)二分图染色完成,此时要完成判断是否可以使偶数点的个数之和为n2,如果满足则就是一种方案了,如果不满足则输出“NO”。因为(1)中染色虽然分了奇偶,但是他们的奇偶染色可以对换颜色,所以每个连通块可以使用Z[i]或O[i]个n2,答案就变成了01背包,询问是否会组成一个体积为n2的背包,其中每个物品的体积就是O[i]或者Z[i]。
(3)01背包完成之后,就可以根据转移路径(直接判断dp[i-1][cur-O[i]]==true即可,但一定
要把cur=cur-O[i](cur-Z[i])放在dfs之前
(重新染色会改变O[i]的数量,Z[i]也同样)重新对每个点进行染色,如果这个转移路径是从上一层经过O[i]转移过来,说明这个连通块使用了奇数个数为n2,则这个连通块的奇偶要进行对换。如果是Z[i]转移则表示还是原来的染色方案。
最后在输出奇数的时候区分1,3,输出答案即可。
解题代码:
#include<bits/stdc++.h>
using namespace std;
mt19937 rng_32(chrono::steady_clock::now().time_since_epoch().count());
typedef long long ll;
const int maxn=2e5+10;
int n1,n2,n3,n;
int m;
bool visit[maxn];
struct E{
int to,nxt;
}e[maxn];
int tot,head[maxn];
void adde(int u,int v)
{
e[++tot]=E{v,head[u]};
head[u]=tot;
}
int num[maxn];
int O1[maxn],Z1[maxn],pat_head[maxn];
int part_num;
bool dfs(int u,int fa)
{
num[u]=num[fa]^1;
if (num[u]==1)
O1[part_num]++;
else
Z1[part_num]++;
for (int i=head[u];i;i=e[i].nxt)
{
if (num[e[i].to]==num[u])
return false;
if (num[e[i].to]==-1)
{
if (dfs(e[i].to,u)==false)
return false;
}
}
return true;
}
bool dp[5005][5005];
int main()
{
cin>>n>>m;
cin>>n1>>n2>>n3;
int t1,t2;
for (int i=1;i<=m;i++)
{
scanf("%d%d",&t1,&t2);
adde(t1,t2);
adde(t2,t1);
}
memset(num,-1,sizeof(num));
num[0]=0;
for (int i=1;i<=n;i++)
{
if (num[i]==-1)
{
part_num++;
pat_head[part_num]=i;
if (!dfs(i,0))
{
cout<<"NO\n";
return 0;
}
}
}
dp[0][0]=1;
for (int i=1;i<=part_num;i++)
{
for (int j=min(Z1[i],O1[i]);j<=n2;j++)
{
if (j>=Z1[i] && dp[i-1][j-Z1[i]])
dp[i][j]=true;
if (j>=O1[i] && dp[i-1][j-O1[i]])
dp[i][j]=true;
}
}
if (dp[part_num][n2])
{
cout<<"YES\n";
memset(num,-1,sizeof(num));
int cur=n2;
for (int i=part_num;i>=1;i--)
{
int sub1,sub2;
if (cur>=O1[i] && dp[i-1][cur-O1[i]])
{
num[0]=1;
//一定要放在dfs前面!!!
cur-=O1[i];
dfs(pat_head[i],0);
}
else
{
cur-=Z1[i];
//一定要放在dfs前面!!!
num[0]=0;
dfs(pat_head[i],0);
}
}
for (int i=1;i<=n;i++)
{
if (num[i])
{
if (n1)
{
cout<<1;
n1--;
}
else
{
cout<<3;
}
}
else
cout<<2;
}
}
else
{
cout<<"NO\n";
}
}