加分二叉树
数据很小, 肯定考率爆搜
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。 试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。
要求输出:
(1)tree的最高加分
(2)tree的前序遍历
中序遍历时1 , 2 , 3 , 4 … n , 说明随便一个点都可以作为根节点,左边就是左子树,右边就是右子树,有这么个性质我们就可以枚举一下根节点。算贡献的话题目已经给出公式了,可以采取记忆化搜索的方式来爆搜
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <map>
#include <list>
#include <queue>
#include <cstring>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <stack>
#include <set>
#pragma GCC optimize(3 , "Ofast" , "inline")
using namespace std ;
typedef long long ll ;
#define ios ios::sync_with_stdio(false), cin.tie(0) ,cout.tie(0)
const double esp = 1e-6 , pi = acos(-1) ;
typedef pair<int , int> PII ;
const int N = 1e6 + 10 , INF = 0x3f3f3f3f , mod = 1e9 + 7;
int in()
{
int x = 0 , f = 1 ;
char ch = getchar() ;
while(!isdigit(ch)) {if(ch == '-') f = -1 ; ch = getchar() ;}
while(isdigit(ch)) x = x * 10 + ch - 48 , ch = getchar() ;
return x * f ;
}
int dp[40][40] , n , path[40][40] , a[N] ;
// path[l][r] 表示l和r的最近公共祖先 , 也就相当于最近的一个根节点
int dfs(int l , int r) // 求l 到 r组成一个数的最大加分数
{
// 如果当前l到r已经被算过了,直接返回
if(dp[l][r] != 0) return dp[l][r] ;
// 这个地方是因为l 大于 r了, 这个时候返回1还要和他的上一级(谁调用的这个区间)相乘,所以不能返回0
if(l > r) return 1 ;
// 每一个叶子子节点的公共祖先就是他自己, 最大加分数也是他自己
if(l == r) {
path[l][r] = l ;
return a[l] ;
}
int &ans = dp[l][r] = 0 ;
for(int i = l ;i <= r ;i ++ )
{
//枚举当前l到r区间的根节点 , 那么左区间就是l ~ (i - 1) , 右区间就是(i + 1) ~ r , 然后加上当前a[i]
int temp = dfs(l , i - 1) * dfs(i + 1 , r) + a[i] ;
// 当前i作为根节点更优
if(temp > ans) ans = temp , path[l][r] = i ;
}
return ans ;
}
void get(int l , int r)
{
if(l > r) return ;
cout << path[l][r] << " " ;
get(l , path[l][r] - 1) , get(path[l][r] + 1 , r) ;
}
int main()
{
ios ;
n = in() ;
for(int i = 1; i <= n ;i ++ ) a[i] = in() ;
cout << dfs(1 , n) << endl ;
get(1 , n) ;
return 0 ;
}
/*
*/