1. 程式人生 > >NOIP2003 洛谷1040 加分二叉樹

NOIP2003 洛谷1040 加分二叉樹

題目描述

設一個n個結點的二叉樹tree的中序遍歷為(1,2,3,…,n),其中數字1,2,3,…,n為結點編號。每個結點都有一個分數(均為正整數),記第i個結點的分數為di,tree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:

subtree×subtreesubtree

若某個子樹為空,規定其加分為1,葉子的加分就是葉結點本身的分數。不考慮它的空子樹。

試求一棵符合中序遍歷為(1,2,3,…,n)且加分最高的二叉樹tree。要求輸出;

  1. tree的最高加分
  2. tree的前序遍歷

輸入格式

第1行:一個整數n(n<30),為結點個數。
第2行:n個用空格隔開的整數,為每個結點的分數(分數<100)。

輸出格式

第1行:一個整數,為最高加分(結果不會超過4,000,000,000)。
第2行:n個用空格隔開的整數,為該樹的前序遍歷。

輸入樣例

5
5 7 1 2 10

輸出樣例

145
3 1 2 4 5

Sol

不妨開兩個陣列。

  • f[l,r]lr
  • root[l,r][l,r]此舉有利於進行DFS操作。

中序:
  根
  / \
 小 大

程式碼

#include <cstdio>
#include <cstring> using namespace std; int f[30][30], root[30][30]; int n; int get(int l, int r) { if (l > r) return 1;//空子樹的值為1,且1對乘積無影響,可直接取1 if (~f[l][r]) return f[l][r];//如果已經遍歷,則可以直接返回值 for (int k = l; k <= r; k++) {//列舉每個結點為樹根的可能性,找出最大的分值 int now = get(l, k - 1) * get(k + 1
, r) + f[k][k]; if (now > f[l][r]) {//找到比當前更大的分值就更新,同時記錄樹根節點 f[l][r] = now; root[l][r] = k; } } return f[l][r];//返回最大分值 } void dfs(int l, int r) { if (l > r) return; printf("%d ", root[l][r]); dfs(l, root[l][r] - 1); dfs(root[l][r] + 1, r); //用root陣列將原樹分段,並以前序遍歷列印 } int main(int argc, char **argv) { scanf("%d", &n); //Initialize memset(f, -1, sizeof f); for (int i = 1; i <= n; i++) { scanf("%d", &f[i][i]);//讀入每個結點的分值 root[i][i] = i;//自己的根結點是自己 } //開始分治 printf("%d\n", get(1, n)); //列印前序遍歷 dfs(1, n); }