[Zjoi2006]三色二叉树(bzoj1864)(洛谷2585)题解
原题地址:https://www.luogu.org/problemnew/show/P2585
题目大意:可以把一个节点染成三种颜色,父节点和两个子节点(可以有一个)颜色不能相同。求最多(少)能有多少个点能被染成绿色
由于是一棵树,多决策的问题,很明显的树形DP。
关于树形DP详见https://www.cnblogs.com/lizitong/p/10020914.html
二维状态,dp[i][j] j取0,1,2表示三种颜色。表示以这个编号为父节点取这个颜色时候有dp[i][j]个点能被染成绿色。
DFS搜到最底,然后给叶节点附上初值。
然后回溯更新。
以最小值为例。
dpmi[x][1] = min(dpmi[x][1],min(dpmi[l[x]][2]+dpmi[r[x]][3],dpmi[l[x]][3]+dpmi[r[x]][2])+1);
dpmi[x][2] = min(dpmi[x][2],min(dpmi[l[x]][1]+dpmi[r[x]][3],dpmi[l[x]][3]+dpmi[r[x]][1]));
dpmi[x][3] = min(dpmi[x][3],min(dpmi[l[x]][1]+dpmi[r[x]][2],dpmi[l[x]][2]+dpmi[r[x]][1]));
如果这个点是绿色,就比较子节点分别为其他两种颜色的大小,然后+1
如果这个点是其他颜色,就比较这两个点子节点分别是另外两种颜色和的大小。
最后输出父节点的最大(小)状态。
分别用l和r数组存左右儿子。
细节较多,上代码。
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#define N 500005
using namespace std;
char c[N];
struct edge
{
int to;
int nxt;
int from;
}eg[N];
int head[N];
int cnt = 1;
int ct = 1;
int dpma[N][4];
int dpmi[N][4];
int l[N];
int r[N];
void add(int x,int y)
{
eg[cnt].to = y;
eg[cnt].nxt = head[x];
eg[cnt].from = x;
head[x] = cnt++;
}
void buildtree(int x)
{
if(c[x]=='2')
{
add(x,++ct);
l[x] = ct;
buildtree(ct);
add(x,++ct);
r[x] = ct;
buildtree(ct);
}else if(c[x]=='1')
{
add(x,++ct);
l[x] = ct;
buildtree(ct);
}else
{
return ;
}
}
void dfsma(int x)
{
if(l[x]==0&&r[x]==0)
{
dpma[x][1] = 1;
return ;
}else
{
if(l[x])
{
dfsma(l[x]);
dpma[x][1] = max(dpma[x][1],max(dpma[l[x]][2]+dpma[r[x]][3],dpma[l[x]][3]+dpma[r[x]][2])+1);
dpma[x][2] = max(dpma[x][2],max(dpma[l[x]][1]+dpma[r[x]][3],dpma[l[x]][3]+dpma[r[x]][1]));
dpma[x][3] = max(dpma[x][3],max(dpma[l[x]][1]+dpma[r[x]][2],dpma[l[x]][2]+dpma[r[x]][1]));
}
if(r[x])
{
dfsma(r[x]);
dpma[x][1] = max(dpma[x][1],max(dpma[l[x]][2]+dpma[r[x]][3],dpma[l[x]][3]+dpma[r[x]][2])+1);
dpma[x][2] = max(dpma[x][2],max(dpma[l[x]][1]+dpma[r[x]][3],dpma[l[x]][3]+dpma[r[x]][1]));
dpma[x][3] = max(dpma[x][3],max(dpma[l[x]][1]+dpma[r[x]][2],dpma[l[x]][2]+dpma[r[x]][1]));
}
}
}
void dfsmi(int x)
{
if(l[x]==0&&r[x]==0)
{
dpmi[x][1] = 1;
dpmi[x][2] = 0;
dpmi[x][3] = 0;
return ;
}else
{
if(l[x])
{
dfsmi(l[x]);
dpmi[x][1] = min(dpmi[x][1],min(dpmi[l[x]][2]+dpmi[r[x]][3],dpmi[l[x]][3]+dpmi[r[x]][2])+1);
dpmi[x][2] = min(dpmi[x][2],min(dpmi[l[x]][1]+dpmi[r[x]][3],dpmi[l[x]][3]+dpmi[r[x]][1]));
dpmi[x][3] = min(dpmi[x][3],min(dpmi[l[x]][1]+dpmi[r[x]][2],dpmi[l[x]][2]+dpmi[r[x]][1]));
}
if(r[x])
{
dfsmi(r[x]);
dpmi[x][1] = min(dpmi[x][1],min(dpmi[l[x]][2]+dpmi[r[x]][3],dpmi[l[x]][3]+dpmi[r[x]][2])+1);
dpmi[x][2] = min(dpmi[x][2],min(dpmi[l[x]][1]+dpmi[r[x]][3],dpmi[l[x]][3]+dpmi[r[x]][1]));
dpmi[x][3] = min(dpmi[x][3],min(dpmi[l[x]][1]+dpmi[r[x]][2],dpmi[l[x]][2]+dpmi[r[x]][1]));
}
}
}
int main()
{
scanf("%s",c+1);
buildtree(1);
memset(dpmi,0x3f,sizeof(dpmi));
dpmi[0][1] = 0;
dpmi[0][2] = 0;
dpmi[0][3] = 0;
dfsma(1);
dfsmi(1);
printf("%d ",max(max(dpma[1][1],dpma[1][2]),dpma[1][3]));
printf("%d",min(min(dpmi[1][1],dpmi[1][2]),dpmi[1][3]));
}