樹的順序遍歷分為先序遍歷、中序遍歷、后序遍歷三種(如果沒有了解過,請參見此處)
上次已經說過,有關樹的順序遍歷的題目還是有點思維難度的,我們先來看一下
題目鏈接A
加分二叉樹
題目描述 Description
設一個n個節點的二叉樹tree的中序遍歷為(l,2,3,…,n),其中數字1,2,3,…,n為節點編號。每個節點都有一個分數(均為正整數),記第j個節點的分數為di,tree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:
subtree的左子樹的加分× subtree的右子樹的加分+subtree的根的分數
若某個子樹為主,規定其加分為1,葉子的加分就是葉節點本身的分數。不考慮它的空
子樹。
試求一棵符合中序遍歷為(1,2,3,…,n)且加分最高的二叉樹tree。要求輸出;
(1)tree的最高加分
(2)tree的前序遍歷
現在,請你幫助你的好朋友XZ設計一個程序,求得正確的答案。
輸入描述 Input Description
第1行:一個整數n(n<=30),為節點個數。
第2行:n個用空格隔開的整數,為每個節點的分數(分數<=100)
輸出描述 Output Description
第1行:一個整數,為最高加分(結果不會超過4,000,000,000)。
第2行:n個用空格隔開的整數,為該樹的前序遍歷。
樣例輸入 Sample Input
5
5 7 1 2 10
樣例輸出 Sample Output
145
3 1 2 4 5
數據范圍及提示 Data Size & Hint
n(n<=30)
分數<=100
這道題特殊的地方在於它沒有給定樹的結構,而是要你求出這棵樹的結構
但我們可以發現,這棵樹的中序遍歷一定滿足from 1 to n,所以我們回想起中序遍歷的性質:
在中序遍歷下,某棵子樹(在該子樹范圍內)的左邊的一定是他的左子樹,右邊的一定是它的右子樹
即,在這道題中:分數=左邊合並的分數*右邊合並的分數+此根節點的的分數
於是,這道題便可以用區間合並做了,Dp方程為:
$$ dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k] (i<k<j) $$
那么第二問呢?
此時,我們可以考慮新開一個數組g[i][j]表示合並區間[i,j]時,使得其得分最大的根節點的編號(枚舉實現)
$$ if(dp[i][j]<dp[i][k-1]*dp[k+1][j]+a[k] (i<k<j)) $$
$$ \ \ \ g[i][j]=k $$
輸出的時候,再按照前序遍歷的規則輸出
void Out(int fa){
printf("%d ",fa);
Out(lson);
Out(rson);
}
上代碼:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=35;
int a[maxn],n;
int dp[maxn][maxn];
int tr[maxn],rt,ans[maxn][maxn],imax;
int read(){
int Value=0,Base=1;char Ch=getchar();
for(;!isdigit(Ch);Ch=getchar())if(Ch=='-')Base=-1;
for(;isdigit(Ch);Ch=getchar())Value=Value*10+(Ch^'0');
return Value*Base;
}
void Out(int l,int r){
if(l>r)return ;
printf("%d ",ans[l][r]);
if(ans[l][r]!=l)Out(l,ans[l][r]-1);
if(ans[l][r]!=r)Out(ans[l][r]+1,r);
}
int main( ){
int m,j,k,i;
n=read();
for(i=0;i<=2*n;i++)
for(j=0;j<=2*n;j++)
dp[i][j]=1;
for(i=1;i<=n;i++)a[i]=read(),dp[i][i]=a[i],ans[i][i]=i;
for(i=n;i>=1;i--){
for(j=i+1;j<=n;j++){
for(k=i;k<=j;k++){
if(dp[i][j]<dp[i][k-1]*dp[k+1][j]+a[k]){
dp[i][j]=dp[i][k-1]*dp[k+1][j]+a[k];
ans[i][j]=k;
}
}
}
}
printf("%d\n",dp[1][n]);
Out(1,n);
puts("");
return 0;
}
現在,如果一下子就AC了,就可以睡覺了就來看看B題吧
題目鏈接B
樹的遍歷
題目描述 Description
我們都很熟悉二叉樹的前序、中序、后序遍歷,在數據結構中常提出這樣的問題:已知一棵二叉樹的前序和中序遍歷,求它的后序遍歷,相應的,已知一棵二叉樹的后序遍歷和中序遍歷序列你也能求出它的前序遍歷。然而給定一棵二叉樹的前序和后序,你卻不能確定其中序遍歷序列,考慮如下圖中的幾棵二叉樹:
所有這些二叉樹都有着相同的前序遍歷和后序遍歷,但中序遍歷卻不相同。
輸入描述 Input Description
輸入文件共2行,第一行表示該樹的前序遍歷結果,第二行表示該樹的后序遍歷結果。輸入的字符集合為{a-z},長度不超過26。
輸出描述 Output Description
輸出文件只包含一個不超過長整型的整數,表示可能的中序遍歷序列的總數。
樣例輸入 Sample Input
abc
cba
樣例輸出 Sample Output
4
數據范圍及提示 Data Size & Hint
如描述
這道題看似沒有任何思路啊~不過其實是結論題,各位可以再想一想
~
~
~
經過觀察,可以發現,當某棵樹某個節點只有一個兒子時,才有可能出現先序遍歷和后序遍歷都相同,而中序遍歷不同的情況
例如:
A
/ \
B D
/
C
如果B節點的左兒子C接在右兒子的位置上,那么先序遍歷和后序遍歷都不會變,但會影響中序遍歷
所以,每有一對這樣的節點,ans就要乘以2,這種情況的表現就是先序遍歷中有一對緊鄰着的AB,而在后序遍歷中是BA
看代碼:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1010;
char a[maxn],b[maxn];
int ans=0;
int main( ){
int m,n,j,k=1,i;
scanf("%s%s",a,b);
for(i=0;i<strlen(a);i++)
for(j=1;j<strlen(b);j++)
if(a[i]==b[j] && a[i+1]==b[j-1])
ans++;
for(i=1;i<=ans;i++)k<<=1;
printf("%d\n",k);
}
那么關於樹的順序遍歷的內容就是這些了~