一棵有點權的有根樹如果滿足以下條件,則被軒軒稱為對稱二叉樹:
下圖中節點內的數字為權值,節點外的 id 表示節點編號。
現在給出一棵二叉樹,希望你找出它的一棵子樹,該子樹為對稱二叉樹,且節點數最多。請輸出這棵子樹的節點數。
注意:只有樹根的樹也是對稱二叉樹。本題中約定,以節點 t 為子樹根的一棵「子樹」指的是:節點 t 和它的全部後代節點構成的二叉樹。
第一行乙個正整數 n,表示給定的樹的節點的數目,規定節點編號 1~n,其中節點1 是樹根。
第二行 n 個正整數,用乙個空格分隔,第 i 個正整數 vi 代表節點 i 的權值。
接下來 n 行,每行兩個正整數 li , ri ,分別表示節點 i 的左右孩子的編號。如果不存在左 / 右孩子,則以 −1 表示。兩個數之間用乙個空格隔開。
輸出共一行,包含乙個整數,表示給定的樹的最大對稱二叉子樹的節點數。
輸入樣例 1
21 3
2 -1
-1 -1
輸入樣例 2
102 2 5 5 5 5 4 4 2 3
9 10
-1 -1
-1 -1
-1 -1
-1 -1
-1 2
3 45 6
-1 -1
7 8輸出樣例 1
1輸出樣例 2
最大的對稱二叉子樹為以節點 2 為樹根的子樹,節點數為 1。
最大的對稱二叉子樹為以節點 7 為樹根的子樹,節點數為 3。
共25個測試點。vi≤1000
測試點1~3,n≤10,保證根結點的左子樹的所有節點都沒有右孩子,根結點的右子樹的所有節點都沒有左孩子。
測試點4~8,n≤10。
測試點9~12,n≤10^5,保證輸入是一棵「滿二叉樹」。
測試點13~16,n≤10^5,保證輸入是一棵「完全二叉樹」。
測試點17~20,n≤10^5,保證輸入的樹的點權均為 1。
測試點21~25,n≤10^6。
這道題看起來難度很大,很多同學不敢去做。但實際上改題的做法很暴力:列舉每個結點,如果它左右子樹大小相同,則暴力 check 一下以這個結點為根的子樹是否合法。
雖然看上去很暴力(複雜度好像是o(n^2)),但實際上這樣做的時間複雜度的確是 o(nlog n)。
證明思路可以採用啟發式合併的時間複雜度證明思路。
即:因為左右子樹相同時才 check,因此每一次 check 樹的大小至少增大一倍。最多 log 次樹的大小就會到達 n,所以每個結點只會被 check log 次。
參考**
#includeusing namespace std;
const int maxn = 1000000;
int siz[maxn + 5], le[maxn + 5], ri[maxn + 5], v[maxn + 5], ans;
bool check(int r1, int r2)
int dfs1(int rt)
void dfs2(int rt)
int main()
dfs1(1);
dfs2(1);
printf("%d\n", ans);
return 0;
}
如果一棵子樹是對稱的,那麼他的中序變數和逆中序遍歷是相同的,也就是子樹的dfs序是回文串。
需要注意的是,不同層次結點是值有可能相同,這樣兒子在左邊或者右邊就判斷不出來(父親結點的值跟兒子一樣),因此,我們可以給結點的值加入層次,如加上層次*1001(超過權值的範圍)。
處理好dfs序和子樹結點數量後,跑一遍manacher匹配最大回文串,如果回文長度跟子樹結點數量相等,那麼就是對稱子樹,記錄最大值。
還有其他做法:雜湊(有衝突怎麼辦?)、爆搜(怎麼剪枝?)
實現**:略。
題解 NOIP2018PJ 對稱二叉樹
在考場上的玄學思路 首先不斷摸索用手划水,用腳蹬水的動作,經過一段時間的練習,你就學會了游泳 這道題的思路相對來說還是很好想出來的 首先,對於每乙個節點,只要右兒子節點與左兒子節點的權值不同,就肯定不是對稱的,捨去 然後,每一次向下走一層的時候,左子樹跟右子樹的路徑取反 什麼意思呢?就是,你左子樹向...
題解 NOIP2018PJ 對稱二叉樹
在考場上的玄學思路 這道題的思路相對來說還是很好想出來的 首先,對於每乙個節點,只要右兒子節點與左兒子節點的權值不同,就肯定不是對稱的,捨去 然後,每一次向下走一層的時候,左子樹跟右子樹的路徑取反 什麼意思呢?就是,你左子樹向右走,右子樹就向左走 左子樹向左走,右子樹向右走 如果這裡不能理解建議動手...
對稱二叉樹 NOIP2018
首先,我們將題目給定的對稱二叉樹的定義來梳理一遍 一棵有點權的有根樹如果滿足以下條件 1.這是一棵二叉樹 2.將這棵樹所有節點的左右子樹交換,新樹和原樹對應位置的結構相同且點權相等。則這顆樹就是一棵對稱二叉樹 我們來看9 12的資料 這是一棵滿二叉樹 所以,對於這一檔資料,我們只要判斷他們的權值是否...