题解 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]));//最后我们只要比较一下,树根染和不染的答案哪个更优
真·撒花
补一个完整代码