[ZJOI2006]三色二叉树 (树形dp)

 

转一个讲的很详细的题解↓清晰又简洁,给跪orz


题解 P2585 【[ZJOI2006]三色二叉树】

Fading posted on 2018-03-17 11:40:53 | under 树形动态规划 | 19


大佬们dp方程没解释清楚啊...

我的dp也是递推实现的,不像大佬们那样用树上dfs

——————分割线——————

首先是建树。

这个树是递归定义的,所以考虑用dfs建树。

就说样例吧。 1122002010

邻接表什么都不存在的!因为是二叉树,所以我们用邻接矩阵会更方便一些。

设tree[i][0] 为第i个节点的左节点编号
设tree[i][1] 为第i个节点的右节点编号

void dfs(int root){//root表示几号节点
    tot++;//表示总共访问了几个节点。
    if (s[root]=='0') return;//叶子节点退出
    if (s[root]=='1') {
        tree[root][0]=root+1;//下一个访问的节点的编号一定是这个节点编号+1
        dfs(root+1);//向下搜
    }
    if (s[root]=='2') {
        tree[root][0]=root+1;
        dfs(root+1);
        tree[root][1]=tot+1;//右节点一定是总共访问的编号+1
        dfs(tot+1);
    }
}

然后导dp方程

先求最大值。这种染色问题比较恶心。在dp的过程中, 我们不知道它的左右结点是否染了绿色,

因此dp出来的结果会有问题???

所以我们可以考虑记录一下dp的状态。

这种方法是我从P1352 没有上司的舞会中学到的,大家可以看看。

设f[i][0]表示 第i个点,不染绿色,其子树(包括自己)染成绿色的最大值。
设f[i][1]表示 第i个点,染绿色,其子树(包括自己)染成绿色的最大值。
那么,首先,第i个点若染绿色,那么其左右节点都不能染绿色。所以考虑把左右节点不染绿色的状态值相加,便得到了结果。
那么f[i][1]=f[tree[i][0]][0]+f[tree[i][1][0]]
若这个点不染绿色呢?
我们会想,这个点不染绿色,那么由题意,它的左右节点不可以同时染绿色,枚举两节点染色情况就可以了。
我们求的是最大值,那么f[i][0]不就是  max(左节点染绿色的值+右节点不染绿色的值,左节点染绿色的值+右节点不染绿色的值,左节点不染绿色的值+右节点不染绿色的值)?
问题解决了,那么最小值也一样。撒花!!!

......有这么轻松???

这道题的坑点来了,对于第i个节点不染绿色的情况,不存在两个节点都不染绿色的情况。

为什么???

如果第i个点以及其子节点都不染绿色,那么,这三个点只能染上红、蓝两色。

然而根据抽屉原理,三个点染上两种颜色,一定有两个点的颜色相同!!!

所以,不符合题意,tan90!不存在的!这就是为什么叫三色二叉树。

最后开始dp

为什么要用dfs?为什么要用dfs?

由于对于一棵子树,其编号排序一定为 根节点<左节点<右节点

所以用递推,没毛病啊。

从节点数开始倒推就可以了。

for (int i=n;i>=1;i--){
    f[i][1]=f[tree[i][0]][0]+f[tree[i][1]][0]+1;
    f[i][0]=max(f[tree[i][0]][1]+f[tree[i][1]][0],f[tree[i][0]][0]+f[tree[i][1]][1]);
}
printf("%d ",max(f[1][1],f[1][0]));//最后我们只要比较一下,树根染和不染的答案哪个更优
for (int i=n;i>=1;i--){
    f[i][1]=f[tree[i][0]][0]+f[tree[i][1]][0]+1;
    f[i][0]=min(f[tree[i][0]][1]+f[tree[i][1]][0],f[tree[i][0]][0]+f[tree[i][1]][1]);
}
printf("%d",min(f[1][1],f[1][0]));//最后我们只要比较一下,树根染和不染的答案哪个更优

真·撒花


补一个完整代码
 

猜你喜欢

转载自www.cnblogs.com/phemiku/p/11430575.html