1583 叶子的染色(CQOI2009 LOJ10161 LUOGU3155 提高+/省选-) 任选非叶节点作根不影响结果的证明 考虑黑白无三种状态的树上DP

总目录

在线测评地址(ybt)

在线测评地址(LOJ)

在线测评地址(LUOGU)

样例对应的树形结构如下:

题意费解,反复读题,总算弄明白题意了。

以节点4为根节点,

要求路径(4,1)离根最远的有色节点是黑色,

路径(4,5,2)离根最远的有色节点是白色,

路径(4,5,3)离根最远的有色节点是黑色,

可以这样安排,让根节点4是黑色,节点2是白色,总共需染色的点是2个。

以节点5为根节点,

要求路径(5,4,1)离根最远的有色节点是黑色,

路径(5,2)离根最远的有色节点是白色,

路径(5,3)离根最远的有色节点是黑色,

可以这样安排,让根节点5是黑色,节点2是白色,总共需染色的点是2个。

可以这样考虑:

自叶节点向根节点推进,

v是u的子节点。

f[u][2]表示节点u不染色对应的最少染色数量,

f[u][0]表示节点u染黑色对应的最少染色数量,

f[u][1]表示节点u染白色对应的最少染色数量,

状态转移方程,想想,想不出,感觉有些乱。

还是从样例入手,试着推出状态转移方程。

以4为根节点

自叶节点向根节点转移。
f[1][0]=1,f[1][1]=0,f[1][2]=0
f[4][0]

f[3][0]=1,f[3][1]=0,f[3][2]=0
f[5][0]

f[2][0]=0,f[2][1]=1,f[2][2]=0
f[5][0]

也就写到这份上,写不下去了。

  • 分析题意:

    随意选择一个不为叶节点的点为根
    对答案没有任何影响(困扰了很久,竟被这么轻松证明,原打算,枚举所有可能的根节点)

  • 证明:类似换根的想法,设一开始的树根为x,现在要将他的某个儿子son设为根。

    根据上面分析,在x做根的最优方案中,

    1.x一定会染色;

    2.x与son不会染相同的颜色。

    不妨设那时x染为黑。

    A.当son染为白时,换根后两者分别可能会影响的范围根本没有变化,所以颜色都不用改变,答案当然不会变。

    B.当son不染色时,原来x可能会影响的范围是整棵树,换根后,只需将x的颜色转给son,x本身不染色即可保证树根可能影响的范围不变,这样只有这两点的颜色交换了,染色总数依然不变。

    所以随便挑一个非叶子节点做根就好了。

1.题意:题目所给出的叶子节点到根(自己定)的最短路径上要有至少一个染色节点,并且必须满足规定的最后一个(即路径上离自己最近)染色节点的颜色为c[u];

2.看到这题,我们应该想到树状dp的基本思路,f[u][]表示以u为根节点的子树满足条件所能获得的(局部)最优解,后一维根据具体题目而定(一般是该节点的状态),然后在深搜中利用儿子的状态更新自己的值,遍历到叶子节点就直接初始化

3.回看这题,无非节点的状态就三种:

一:黑色 二:白色 三:无色

那么得出dp数组f[u][3],表示以u为根节点的子树所要染色节点数的最小值

一开始所有节点,不论选黑选白都至少选了一个有色节点,赋值为1;但对于叶子节点,将他所不能第一个遇到的那个颜色赋值为INF,后面转移时就不会用到这个状态了。

为什么叶节点f[][2]初始赋值为1,非叶节点f[][2]初始赋值为0,终归是状态方程转移的需要。

如果x,son同色,则让x保留颜色,son删除颜色更优(x包含的范围更广)。如果不同色则都保留。两者取较小值传给x。

那么转移方程是什么呢

son为其儿子
f[x][0]+=min(f[son][0]-1,min(f[son][1],f[son][2]));
f[x][1]+=min(f[son][0],min(f[son][1]-1,f[son][2])); 
f[x][2]+=min(f[son][0],min(f[son][1],f[son][2]));

靠自己想,f[son][1]-1或是f[son][0]-1 这点是难想到的。

注意:此题重点其实并不在于(个人见解)方程,而是边界条件!!

深搜的时候,当遍历到叶子节点,它的dp数组值其实早已可以确定,拿一个例子来说

if(c[x])
{f[x][1]=1;  f[x][0]=INF; f[x][2]=1;}

c[x]=1进入,即需要白色,f[x][0]为INF?,我们知道,如果x节点染成黑色,那么就不满足题意,此状态是矛盾的,所以我们必须无视它,怎么办,将其置为无穷大就不会用到这个值了。

通过对样例的模拟,才能对上述有深入的理解:

以4为根节点
f[1][0]=1,f[1][1]=INF,f[1][2]=1
f[4][0]=1,f[4][1]=1,f[4][2]=0
f[4][0]+=min(f[1][2],min(f[1][0]-1,f[1][1])),f[4][0]=1
f[4][1]+=min(f[1][2],min(f[1][1]-1,f[1][0])),f[4][1]=2
f[4][2]+=min(f[1][2],min(f[1][0],f[1][1])),f[4][2]=1

f[3][0]=1,f[3][1]=INF,f[3][2]=1
f[5][0]=1,f[5][1]=1,f[5][2]=0
f[5][0]+=min(f[3][2],min(f[3][0]-1,f[3][1])),f[5][0]=1
f[5][1]+=min(f[3][2],min(f[3][1]-1,f[3][0])),f[5][1]=2
f[5][2]+=min(f[3][2],min(f[3][0],f[3][1])),f[5][2]=1

f[2][0]=INF,f[2][1]=1,f[2][2]=1
f[5][0]+=min(f[2][2],min(f[2][0]-1,f[2][1])),f[5][0]=2
f[5][1]+=min(f[2][2],min(f[2][1]-1,f[2][0])),f[5][1]=2
f[5][2]+=min(f[2][2],min(f[2][0],f[2][1])),f[5][2]=1

f[4][0]+=min(f[5][2],min(f[5][0]-1,f[5][1])),f[4][0]=2
f[4][1]+=min(f[5][2],min(f[5][1]-1,f[5][0])),f[4][1]=2
f[4][2]+=min(f[5][2],min(f[5][0],f[5][1])),f[4][2]=2

min(f[4][2],min(f[4][0],f[4][1]))=2

总算有些感觉了。

ybt

通过

测试点结果内存时间
测试点1答案正确636KB2MS
测试点2答案正确632KB1MS
测试点3答案正确636KB2MS
测试点4答案正确640KB2MS
测试点5答案正确644KB2MS
测试点6答案正确664KB2MS
测试点7答案正确784KB3MS
测试点8答案正确928KB3MS
测试点9答案正确980KB4MS
测试点10答案正确984KB4MS

LOJ

LUOGU

AC代码如下:

#include <bits/stdc++.h>
#define maxm 10010
#define INF 20000
using namespace std;
struct node {
	int to,next;
} e[maxm<<1];
int head[maxm],tot;
int f[maxm][3],m,n,c[maxm];
void add(int u,int v) {
	tot++,e[tot].to=v,e[tot].next=head[u],head[u]=tot;
}
void init() {
	int i,u,v;
	scanf("%d%d",&m,&n);
	for(i=1; i<=n; i++)scanf("%d",&c[i]);
	for(i=1; i<m; i++) {
		scanf("%d%d",&u,&v);
		add(u,v),add(v,u);
	}
	for(i=1; i<=m; i++)f[i][0]=f[i][1]=1;
}
void dfs(int u,int fa) {
	int i,v;
	for(i=head[u]; i; i=e[i].next) {
		v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
		if(v<=n)f[v][!c[v]]=INF,f[v][2]=1;
		f[u][0]+=min(f[v][2],min(f[v][0]-1,f[v][1]));
		f[u][1]+=min(f[v][2],min(f[v][0],f[v][1]-1));
		f[u][2]+=min(f[v][2],min(f[v][0],f[v][1]));
	}
}
int main() {
	init();
	dfs(n+1,-1);
	printf("%d\n",min(f[n+1][2],min(f[n+1][0],f[n+1][1])));
	return 0;
}

 该题习得什么?

换根思维。

很不一样的状态转移方程,尤其是-1这项。

叶节点,根节点f[][2]的赋值不同。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值