洛谷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;
}