BZOJ 1304 叶子的染色 树形DP

本文介绍了一种使用树形动态规划解决特定染色问题的方法。通过对树状结构的节点进行染色,使得相邻节点的颜色不同,且整个染色方案达到最小染色数目的最优解。文章详细解释了状态定义、状态转移方程,并提供了完整的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

树形DP

开始的时候似乎并没有什么想法,所以我们来观察一下他的性质,便于状态的转移

首先,对于同一个节点及其子树,并不会在他的儿子上同时存在两个矛盾。因为如果同时存在,不管前面放哪个颜色,都会使另一个不合法

其次,大胆YY,对于一个已经安排好的树来说,从任何一个合法的点中取出一个做根总是合法的

有了这样的性质,我们不妨进行这样的设计

f[i]i
g[i]i
h[i]i

在DFS之后进行状态转移

x=f[v]y=min(f[v],g[v])z=min(f[v],h[v])vson[i]

这样的话就可以很轻松的进行转移
f[i]=min(x,y+1,z+1)g[i]=min(y,z+1)h[i]=min(y+1,z)

最后的答案当然就是
f[root]

注意递归边界时的更新

下面是代码

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#define N 100000+5
#define M 200000+5
using namespace std;
int head[N],f[N],g[N],h[N],m,n;
int to[N];
//f[i] represent the formal least nums && the root is i
//g[i] represent the informal least nums because of the white leaves && the root is i
//h[i] represent the informal least nums because of the black leaves && the root is i
//of all his son-trees there can't be conflick or it will go die
//we make tmp1=sum{f[v]}  v is one son of i 
//        tmp2=sum{min(f[v],g[v])} v is one son of i 
//        tmp3=sum{min(f[v]),h[v]} v is one son of i
//then f[i]=min(tmp1,tmp2+1,tmp3+1)
//     g[i]=min(tmp2,tmp3+1)
//     h[i]=min(tmp2+1,tmp3)
//     ans=f[root]
char getc()
{
    static const int LEN = 1<<15;
    static char buf[LEN],*S=buf,*T=buf;
    if(S == T)
    {
        T = (S=buf)+fread(buf,1,LEN,stdin);
        if(S == T)return EOF;
    }
    return *S++;
}

int read()
{
    static char ch;
    static int D;
    while(!isdigit(ch=getc()));
    for(D=ch-'0'; isdigit(ch=getc());)
        D=(D<<3)+(D<<1)+(ch-'0');
    return D;
}
int color[N];
struct graph
{
    int next,to;
    graph () {}
    graph (int _next,int _to)
    :next(_next),to(_to){}
}edge[M];
inline void add(int x,int y)
{
    static int cnt = 0;
    edge[++cnt]=graph(head[x],y);
    head[x]=cnt;
    edge[++cnt]=graph(head[y],x);
    head[y]=cnt;
}
void DFS(int x,int fa)
{
    int tmp1=0,tmp2=0,tmp3=0;
    if(color[x]==0)
    {
        f[x]=1,g[x]=0;
        return;
    }
    else if(color[x]==1)
    {
        f[x]=1,h[x]=0;
        return;
    }
    for(int i=head[x];i;i=edge[i].next)
        if(edge[i].to^fa)
        {
            DFS(edge[i].to,x);
            tmp1+=f[edge[i].to];
            tmp2+=min(f[edge[i].to],g[edge[i].to]);
            tmp3+=min(f[edge[i].to],h[edge[i].to]);
        }
    f[x]=min(tmp1,min(tmp2+1,tmp3+1));
    g[x]=min(tmp2,tmp3+1);
    h[x]=min(tmp3,tmp2+1);
}
int main()
{
    cin>>m>>n;
    memset(color,-1,sizeof color);
    //memset(f,0x3f,sizeof f);
    memset(g,0x3f,sizeof g);
    memset(h,0x3f,sizeof h);
    for(int i=1;i<=n;++i)
        color[i]=read();
    int root=-1;
    for(int i=1;i<m;++i)
    {
        int tmp1=read(),tmp2=read();
        add(tmp1,tmp2);
        if(root==-1)
        {
            to[tmp1]++,to[tmp2]++;
            if(to[tmp1]>1)
                root=tmp1;
            else if(to[tmp2]>1)
                root=tmp2;
        }
    }
    DFS(root,-1);
    cout<<f[root];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值