原題連接ios
題目所求是一棵符合中序遍歷且加分最高的二叉樹, 而二叉樹的加分 \(=\) 左子樹的加分 \(×\) 右子樹的加分 \(+\) 根的分數
假設求一棵根節點是\(k\)的加分最高的二叉樹,因爲根的分數已經肯定,則要使其左子樹加分最高且右子樹加分最高
如何使其左子樹加分最高呢?(右子樹同理)
首先要在\([1,k-1]\)(爲何是\([1,k-1]?\) 由於在中序序列中, 根節點左邊是其左子樹, 右邊是其右子樹)枚舉左子樹的根\(j\), 且使以\(j\)爲根節點的二叉樹的左子樹加分最高且右子樹加分最高.
因此求二叉樹的最高加分實際上是一個不斷進行遞歸的過程, 能夠用區間DP
來解決
區間DP
思路以下:ide
題目還讓求一個加分最高的二叉樹的前序遍歷, 且字典序最小
如何求前序遍歷? 在區間DP
的過程當中,每一次從\([i,j]\)劃分出的若干類中選出的最優的第\(k\)類, 其實就能夠肯定\([i,j]\)的根節點是\(k\), 再肯定其左右子樹的根節點, 從而遞歸求出前序遍歷
如何保證字典序最小? 注意到求前序遍歷的過程其實是選擇根節點的過程, 則保證每次選出的根節點最小便可spa
看不懂就參考Y總視頻講解code
#include <cstdio> #include <iostream> #include <algorithm> using namespace std; const int N = 35; int n; int a[N]; int f[N][N]; // f[i][j] 表示 中序遍歷是[i,j]全部二叉樹的集合 的最大加分 int root[N][N]; // 記錄[i,j]的根節點 void dfs(int l, int r) { if(l > r) return; int k = root[l][r]; cout << k << " "; dfs(l,k-1); dfs(k+1,r); return; } int main() { cin >> n; for(int i=1; i<=n; i++) cin >> a[i]; for(int len = 1; len <= n; len++) // 枚舉區間長度 { for(int i=1; i + len - 1 <= n; i++) // 枚舉區間左端點 { int j = i + len - 1; // 肯定區間右端點 for(int k=i; k<=j; k++ ) // 枚舉根節點 { int left_score = k==i ? 1 : f[i][k-1]; // 肯定左子樹的加分(當左子樹爲空(k == i), 規定加分爲1) int right_score = k==j ? 1 : f[k+1][j]; // 肯定右子樹的加分 // 肯定以K爲根節點的二叉樹的加分,葉子(i==j)的加分就是葉節點自己的分數,不考慮它的空子樹。 int k_score = i == j ? a[k] : left_score * right_score + a[k]; if(f[i][j] < k_score) { f[i][j] = k_score; root[i][j] = k; } } } } cout << f[1][n] << endl; dfs(1,n); // 輸出前序序列 system("pause"); return 0; }