有一个显然的性质,即1,3之间可以互换,我们不妨先归之为一类。2是另外一类。
给定一个无向联通图,如何用给定的01数量进行01着色?
不难发现,任意选定一点开始着色后,01的数量即可确定。
可以从任意一点开始dfs,深度为奇数的点为一部分,深度为偶数为另一部分。
有个坑点,还要判是否存在奇环(长度为奇数),这样的环无法进行01染色。
这样着色后会得到两个值,你可以任选其一为0,另一为1.。
现在把难度升级一下就是题目的要求,即为n个联通分量01数量和恒定的情况下,在n个联通分量中分配01。
假定对于第i个连通分量用到0的数量为ai,用到1的数量为bi,注意ai和bi可以互换
那么 ∑ i = 1 c n t a i \sum\limits_{i=1}^{cnt}a_i i=1∑cntai=n1+n3并且 ∑ i = 1 c n t b i \sum\limits_{i=1}^{cnt}b_i i=1∑cntbi=n2
是不是有点背包dp的味道了?
那么存在性的问题就可以解决了
用背包dp一下,对于每一个i(i<=cnt,cnt为连通块数量),加上ai或者bi。最终是否存在一个方案最后总和为n1+n3.
那么在证明了存在的情况下,如何记录每个连通块0染色的个数呢?(本道题难点在于:在DP中记录路径)
可以开一个二维dp数组
dp[5000][5000],dp[i][j]表示到了第i个连通分量能否达到j个0染色数量
dp[i][j]有三个值,-1表示无法到达,0表示加上ai后可以到达,1表示加上bi后可以到达。
这样dp完以后就可以通过判断dp[cnt][n[1]]是否为-1来判断是否能达到。
同时,1和0记录了路径,因为无后效性,所以你可以从最后的终点回溯至起点。
代码如下
#include
using namespace std;
int num[3];
#define MAXN 5005
vectore[MAXN];
vectorcolor[MAXN][2];
int ans[MAXN];
int dp[MAXN][MAXN];
int cnt=0;
int dep[MAXN];
int a[MAXN];
int b[MAXN];
int error=0;
void dfs(int u,int c)
{
color[cnt][c%2].push_back(u);
for(auto it:e[u])
{
if(!dep[it])
{
dep[it]=c+1;
dfs(it,c+1);
}
else
{
if(dep[it]%2==dep[u]%2)error=1;
}
}
}
int main()
{
//freopen("D://tt.txt","r",stdin);
int n,m;
scanf("%d%d",&n,&m);
for(int i=0;i<3;i++)
scanf("%d",num+i);
for(int i=0;i=1;i--)
{
int flag=dp[i][now];
for(int j=0;j0)ans[i]=1,num[0]--;
else ans[i]=3;
}
for(int i=1;i<=n;i++)
printf("%d",ans[i]);
}
else
cout<<"NO"<