Educational Codeforces Round 87 (Rated for Div. 2)E. Graph Coloring详细解析

有一个显然的性质,即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=1cntai=n1+n3并且 ∑ i = 1 c n t b i \sum\limits_{i=1}^{cnt}b_i i=1cntbi=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"<

你可能感兴趣的:(DP,染色)