NOIP2003提高組 加分二叉樹 區間DP+記錄方案

題目描述
設一個n個節點的二叉樹tree的中序遍歷爲(1,2,3,…,n),其中數字1,2,3,…,n爲節點編號。

每個節點都有一個分數(均爲正整數),記第i個節點的分數爲di,tree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:

subtree的左子樹的加分 × subtree的右子樹的加分 + subtree的根的分數

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

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

要求輸出:

(1)tree的最高加分

(2)tree的前序遍歷

輸入格式
第1行:一個整數n,爲節點個數。

第2行:n個用空格隔開的整數,爲每個節點的分數(0<分數<100)。

輸出格式
第1行:一個整數,爲最高加分(結果不會超過4,000,000,000)。

第2行:n個用空格隔開的整數,爲該樹的前序遍歷。如果存在多種方案,則輸出字典序最小的方案。

數據範圍
n<30
輸入樣例:
5
5 7 1 2 10
輸出樣例:
145
3 1 2 4 5

思路
在這裏插入圖片描述

代碼

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 35;

int f[N][N], g[N][N];
int n;
int w[N];

void dfs(int l, int r)
{
    if(l > r) return;
    cout << g[l][r] << " ";
    dfs(l, g[l][r] - 1);
    dfs(g[l][r] + 1, r);
}

int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
		cin >> w[i];
		
	for(int len = 1; len <= n; len++)
		for(int l = 1; l + len - 1 <= n; l++)
		{
			int r = l + len - 1;
			if(len == 1)
			{
				f[l][r] = w[l];
				g[l][r] = l;
			}
			else
			{
				for(int k = l; k <= r; k++)
				{
					int left, right;
					if(k == l)
						left = 1;
					else left = f[l][k - 1];
					if(k == r)
						right = 1;
					else right = f[k + 1][r];
					int sore = left * right + w[k];
					if(sore > f[l][r])
					{
						f[l][r] = sore;
						g[l][r] = k;
					}
				}
			}
		}
	cout << f[1][n] << endl;
	dfs(1, n);
	return 0;	
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章