NOIP2003 洛谷1040 加分二叉樹
阿新 • • 發佈:2019-01-23
題目描述
設一個n個結點的二叉樹tree的中序遍歷為(1,2,3,…,n),其中數字1,2,3,…,n為結點編號。每個結點都有一個分數(均為正整數),記第i個結點的分數為di,tree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:
若某個子樹為空,規定其加分為1,葉子的加分就是葉結點本身的分數。不考慮它的空子樹。
試求一棵符合中序遍歷為(1,2,3,…,n)且加分最高的二叉樹tree。要求輸出;
- tree的最高加分
- 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]為從l結點到r結點的最大分數。 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);
}