1. 程式人生 > 實用技巧 >洛谷P2585&ZJOI 2006-三色二叉樹(樹的染色-樹形DP)

洛谷P2585&ZJOI 2006-三色二叉樹(樹的染色-樹形DP)

題目連結:https://www.luogu.com.cn/problem/P2585
CSDN食用連結:https://blog.csdn.net/qq_43906000/article/details/107965283

題目描述

一棵二叉樹可以按照如下規則表示成一個由0,1,2組成的字元序列,我們稱之為“二叉樹序列S”:
\(S=\left\{\begin{matrix} 0 & 表示該樹沒有子節點\\ 1S_1 & 表示該樹有一個子節點,S_1為其子樹的二叉樹序列\\ 2S_1S_2 &表示該樹由兩個子節點,S_1和S_2分別表示其兩個子樹的二叉樹序列 \end{matrix}\right.\)


例如,下圖所表示的二叉樹序列\(S=21200110\)來表示。

你的任務是要對一棵二叉樹的節點進行染色。每個節點可以被染成紅色、綠色或藍色。並且,一個節點與其子節點的顏色必須不同,如果該節點有兩個子節點,那麼這兩個子節點的顏色也必須不同。給定一顆二叉樹的二叉樹序列,請求出這棵樹中最多和最少有多少個點能夠被染成綠色。

輸入格式
輸入只有一行一個字串 ss,表示二叉樹序列。

輸出格式
輸出只有一行,包含兩個數,依次表示最多和最少有多少個點能夠被染成綠色。

輸入
1122002010
輸出
5 2

說明/提示
資料規模與約定

對於全部的測試點,保證\(1\leq |s|\leq5\times 10^5\),s只含字元0,1,2。

emmm,題難到不難,就是輸入的處理噁心了點。。我們可以遞迴建一下樹,可參考如下程式碼:

int dfs_wedge(char *s,int fa)
{
    if (!s[0]) return 0;
    int nb=1;
    cnt++;
    if (fa!=-1){
        g[cnt].push_back(fa);
        g[fa].push_back(cnt);
    }
    if (s[0]=='0') return nb;
    int u=cnt;
    nb+=dfs_wedge(s+1,u);
    if (s[0]=='2') nb+=dfs_wedge(s+nb,u);
    return nb;
}

那麼接下來就是處理染色的問題了,對於最大的情況我們很容易考慮,和之前上司的舞會,最小點覆蓋的情況類似,直接記錄一下將其染成綠色和不染成綠色所需要的最大值就好了。實際上也就是將當前點染色的時候,所有的兒子不能染色,也就是\(dp[u][1]=1+\sum dp[v][0]\),而當前點不染色的時候,兒子的情況就是要麼全都不染色,要麼只染其中一個,不能全都染(題目要求的。。。)那麼也就是說如果有兩個兒子的話就是\(dp[u][0]=max(dp[v1][0]+dp[v2][0],max(dp[v1][0]+dp[v2][1],dp[v1][1]+dp[v2][0]))\),如果只有一個兒子的話,我們直接取它的最大值就好了:$ dp[u][0]=max(dp[v1][0],dp[v1][1])$那麼我們就很容易得出如下程式碼:

void dfs(int u,int fa)
{
    int v1=0,v2=0;
    for (int i=0; i<g[u].size(); i++){
    	int v=g[u][i];
        if (v==fa) continue;
        dfs(v,u);
        dp[u][1]+=dp[v][0];
        if (v1) v2=v;
        else v1=v;
    }
    dp[u][1]++;
    if (v2) {
        dp[u][0]=max(dp[v1][0]+dp[v2][0],max(dp[v1][0]+dp[v2][1],dp[v1][1]+dp[v2][0]));
    }
    else if (v1) dp[u][0]=max(dp[v1][0],dp[v1][1]);
}

接下來考慮最小的情況。。。這個感覺有點糟。。。。我能全都不染色的嗎?顯然不行,如果一個節點有兩個兒子,這種情況是必須要有綠色的,所以我們在對這種節點不染色的時候要注意,不能兩個兒子都沒有染,所以也就是必須有一個染色了:\(dp[u][0]=min(dp[v1][1]+dp2[v2][0],dp[v1][0]+dp[v2][1])\)而如果給當前節點染色。。。那麼和max的情況就一樣了。

以下是AC程式碼:

#include <bits/stdc++.h>
using namespace std;

#define debug printf("#@$@%\n")
const int mac=5e5+10;

char s[mac];
vector<int>g[mac];
int cnt=0,dp[mac][3];
int dp2[mac][3];

int dfs_wedge(char *s,int fa)
{
    if (!s[0]) return 0;
    int nb=1;
    cnt++;
    if (fa!=-1){
        g[cnt].push_back(fa);
        g[fa].push_back(cnt);
    }
    if (s[0]=='0') return nb;
    int u=cnt;
    nb+=dfs_wedge(s+1,u);
    if (s[0]=='2') nb+=dfs_wedge(s+nb,u);
    return nb;
}

void dfs(int u,int fa)
{
    int v1=0,v2=0;
    for (int i=0; i<g[u].size(); i++){
    	int v=g[u][i];
        if (v==fa) continue;
        dfs(v,u);
        dp[u][1]+=dp[v][0];
        dp2[u][1]+=dp2[v][0];
        if (v1) v2=v;
        else v1=v;
    }
    dp[u][1]++; dp2[u][1]++;
    if (v2) {
        dp2[u][0]=min(dp2[v1][1]+dp2[v2][0],dp2[v1][0]+dp2[v2][1]);
        dp[u][0]=max(dp[v1][0]+dp[v2][0],max(dp[v1][0]+dp[v2][1],dp[v1][1]+dp[v2][0]));
    }
    else if (v1) dp[u][0]=max(dp[v1][0],dp[v1][1]),dp2[u][0]=min(dp2[v1][0],dp2[v1][1]);
}

int main(int argc, char const *argv[])
{
    scanf ("%s",s+1);
    if (s[1]=='0') {printf("%d %d\n",1,0); return 0;}
    dfs_wedge(s+1,-1);
    int n=strlen(s+1);
    dfs(1,-1);
    printf("%d %d\n",max(dp[1][0],dp[1][1]),min(dp2[1][0],dp2[1][1]));
    return 0;
}