1. 程式人生 > 其它 >479. 加分二叉樹

479. 加分二叉樹

題目連結

479. 加分二叉樹

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

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

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

若某個子樹為空,規定其加分為 \(1\)

葉子的加分就是葉節點本身的分數,不考慮它的空子樹。

試求一棵符合中序遍歷為\((1,2,3,…,n)\)

且加分最高的二叉樹 tree。

要求輸出:

(1)tree的最高加分

(2)tree的前序遍歷

輸入格式

\(1\) 行:一個整數 \(n\),為節點個數。

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

輸出格式

\(1\) 行:一個整數,為最高加分(結果不會超過int範圍)。

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

資料範圍

\(n<30\)

輸入樣例:

5
5 7 1 2 10

輸出樣例:

145
3 1 2 4 5

解題思路

區間dp

  • 狀態表示:\(f[l][r]\)

    表示中序遍歷中區間 \([l,r]\) 內組成的樹的最高加分

  • 狀態計算:\(f[l][r]=max(f[l][r],f[l][k-1]*f[k+1][r]+w[k]\),其中 \(k\) 為該區間組成的樹

帶著區間和權值反著推即可得整棵樹的結構

  • 時間複雜度:\(O(n^2)\)

程式碼

// Problem: 加分二叉樹
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/481/
// Memory Limit: 128 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=35;
int n,w[N],f[N][N];
void dfs(int x,int l,int r)
{
	for(int i=l;i<=r;i++)
		if(i==l)
		{
			if(x==f[i+1][r]+w[i])
			{
				cout<<i<<' ';
				dfs(f[i+1][r],i+1,r);
				return ;
			}
		}
		else if(i==r)
		{
			if(x==f[l][i-1]+w[i])
			{
				cout<<i<<' ';
				dfs(f[l][i-1],l,i-1);
				return ;
			}
		}
		else if(x==f[l][i-1]*f[i+1][r]+w[i])
		{
			cout<<i<<' ';
			dfs(f[l][i-1],l,i-1);
			dfs(f[i+1][r],i+1,r);
			return ;
		}
}
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)cin>>w[i];
    for(int i=1;i<=n;i++)f[i][i]=w[i];
    for(int len=2;len<=n;len++)
    	for(int l=1;l+len-1<=n;l++)
    	{
    		int r=l+len-1;
    		for(int k=l;k<=r;k++)
    		{
    			if(k==l)
    				f[l][r]=max(f[l][r],f[k+1][r]+w[k]);
    			else if(k==r)
    				f[l][r]=max(f[l][r],f[l][k-1]+w[k]);
    			else
    				f[l][r]=max(f[l][r],f[l][k-1]*f[k+1][r]+w[k]);
    		}
    	}
    cout<<f[1][n]<<'\n';
    dfs(f[1][n],1,n);
    return 0;
}