POI 1999 三色二叉樹 Three−coloring of binary trees
首先是根據輸入的前序遍歷建樹,然後樹形動態規劃,遞歸地建立二叉樹。假設綠色爲0,紅色爲1,藍色爲2。要求兩遍動態規劃。
以最大值爲例,動態規劃狀態設定
- F[i,c] 以節點i爲根的子樹中,根的顏色爲c時,這棵子樹上顏色爲0的節點的個數。
狀態轉移方程
如果i爲葉節點
- F[i,c]=1 (c爲0)
- F[i,c]=0 (c不爲0)
如果i只有一個子節點
- F[i,0]=Max { F[i.left,1] , F[i.left,2] } + 1
- F[i,1]=Max { F[i.left,0] , F[i.left,2] }
- F[i,2]=Max { F[i.left,0] , F[i.left,1] }
如果i有兩個子節點
- F[i,0]=Max { F[i,left,1]+F[i.right,2] , F[i,left,2]+F[i.right,1] } + 1
- F[i,1]=Max { F[i,left,0]+F[i.right,2] , F[i,left,2]+F[i.right,0] }
- F[i,2]=Max { F[i,left,0]+F[i.right,1] , F[i,left,1]+F[i.right,0] }
目標狀態
- Max{ F[root,0] , F[root,1] , F[root,2] } (root爲根)
以上爲求最大值,求最小值方法一樣,把Max全部改成Min即可。
/*
* Problem: POI1999 trot
* Author: Guo Jiabao
* Time: 2008.12.14 17:09:22
* State: Solved
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
using namespace std;
const int MAX=10002;
const int INF=0x7FFFFFFF;
class node
{
public:
int c,p;
node *l,*r;
node() : l(0),r(0){}
};
char S[MAX];
node E[MAX],*root;
int L,N,Q,Ans1,Ans2;
int F[MAX][3];
bool (*compare)(int,int);
bool bigger(int a,int b) { return a>b; }
bool smaller(int a,int b) { return a<b; }
void build(node *P)
{
Q++;
int k=S[Q]-'0';
P->c=k;
if (k)
{
N++;
P->l=&E[N];
E[N].p=N;
build(P->l);
if (k==2)
{
N++;
P->r=&E[N];
E[N].p=N;
build(P->r);
}
}
}
void init()
{
freopen("trot.in","r",stdin);
freopen("trot.out","w",stdout);
scanf("%s",S);
E[N=1].p=1; Q=-1;
root=&E[1];
build(root);
}
int dp(node *P,int C)
{
int L,R;
if (P->c==0) //leaf
{
if (C==0)
return 1;
else
return 0;
}
else if (P->c==1) //link
{
L=P->l->p;
if (F[L][0]==-1)
F[L][0]=dp(P->l,0);
if (F[L][1]==-1)
F[L][1]=dp(P->l,1);
if (F[L][2]==-1)
F[L][2]=dp(P->l,2);
if (C==0)
{
if (compare(F[L][1] , F[L][2]))
return F[L][1] + 1;
else
return F[L][2] + 1;
}
else if (C==1)
{
if (compare(F[L][0] , F[L][2]))
return F[L][0];
else
return F[L][2];
}
else
{
if (compare(F[L][0] , F[L][1]))
return F[L][0];
else
return F[L][1];
}
}
else
{
L=P->l->p; R=P->r->p;
if (F[L][0]==-1)
F[L][0]=dp(P->l,0);
if (F[L][1]==-1)
F[L][1]=dp(P->l,1);
if (F[L][2]==-1)
F[L][2]=dp(P->l,2);
if (F[R][0]==-1)
F[R][0]=dp(P->r,0);
if (F[R][1]==-1)
F[R][1]=dp(P->r,1);
if (F[R][2]==-1)
F[R][2]=dp(P->r,2);
if (C==0)
{
if (compare(F[L][1]+F[R][2] , F[L][2]+F[R][1]))
return F[L][1]+F[R][2] + 1;
else
return F[L][2]+F[R][1] + 1;
}
else if (C==1)
{
if (compare(F[L][0]+F[R][2] , F[L][2]+F[R][0]))
return F[L][0]+F[R][2];
else
return F[L][2]+F[R][0];
}
else
{
if (compare(F[L][0]+F[R][1] , F[L][1]+F[R][0]))
return F[L][0]+F[R][1];
else
return F[L][1]+F[R][0];
}
}
}
void rin()
{
int i;
for (i=1;i<=N;i++)
F[i][0]=F[i][1]=F[i][2]=-1;
}
void solve()
{
Ans1=0;Ans2=INF;
rin();
compare=bigger;
F[1][0]=dp(root,0);if (F[1][0] > Ans1) Ans1=F[1][0];
F[1][1]=dp(root,1);if (F[1][1] > Ans1) Ans1=F[1][1];
F[1][2]=dp(root,2);if (F[1][2] > Ans1) Ans1=F[1][2];
rin();
compare=smaller;
F[1][0]=dp(root,0);if (F[1][0] < Ans2) Ans2=F[1][0];
F[1][1]=dp(root,1);if (F[1][1] < Ans2) Ans2=F[1][1];
F[1][2]=dp(root,2);if (F[1][2] < Ans2) Ans2=F[1][2];
}
int main()
{
init();
solve();
printf("%d %d",Ans1,Ans2);
return 0;
}
<h2><span class="mw-headline">三色二叉樹</span></h2>
問題描述
一棵二叉樹可以按照如下規則表示成一個由0、1、2組成的字符序列,我們稱之爲“二叉樹序列S”:
- 0 該樹沒有子節點
- 1S1 該樹有一個子節點,S1爲其二叉樹序列
1S1S2 該樹有兩個子節點,S1,S2分別爲兩個二叉樹的序列
例如,下圖所表示的二叉樹可以用二叉樹序列S=21200110來表示。
你的任務是要對一棵二叉樹的節點進行染色。每個節點可以被染成紅色、綠色或藍色。並且,一個節點與其子節點的顏色必須不同,如果該節點有兩個子節點,那麼這兩個子節點的顏色也必須不相同。給定一棵二叉樹的二叉樹序列,請求出這棵樹中最多和最少有多少個點能夠被染成綠色。
輸入文件 輸入文件僅有一行,不超過10000個字符,表示一個二叉樹序列。
輸出文件
輸出文件也只有一行,包含兩個數,依次表示最多和最少有多少個點能夠被染成綠色。
樣例輸入
1122002010
樣例輸出5 2