[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]));    
}

 

posted @ 2018-11-26 16:16  lizitong  阅读(163)  评论(0编辑  收藏  举报