P1040 加分二叉树
题意:
给定一个中序遍历为1,2,3,…,n的二叉树 每个结点有一个权值 定义二叉树的加分规则为: 左子树的加分× 右子树的加分+根的分数 若某个树缺少左子树或右子树,规定缺少的子树加分为1。 构造符合条件的二叉树 该树加分最大 输出其前序遍历序列
想法:
中序遍历的方法是 左子树 根 右子树
因此二叉树左孩子遍历序列一定在根结点的左边,右孩子遍历序列一定在根结点的右边! 因此,假设二叉树的根结点为k,那么中序遍历为1,2,…,n的遍历序列,左孩子序列为1,2,…,k-1,右孩子序列为k+1,k+2,…,n
设f(i,j)中序遍历为i,i+1,…,j的二叉树的最大加分,则有: f(i,j)=max{f[i,k-1]*f[k+1,j] +d[k]} 显然 f(i,i)=d[i] 答案为f(1,n)
枚举根节点就好了
#include <iostream> #include <algorithm> #include <string> #include <string.h> #include <vector> #include <map> #include <stack> #include <set> #include <queue> #include <math.h> #include <cstdio> #include <iomanip> #include <time.h> #include <bitset> #include <cmath> #define LL long long #define INF 0x3f3f3f3f #define ls nod<<1 #define rs (nod<<1)+1 const double eps = 1e-10; const int maxn = 100; const LL mod = 1e9 + 7; int sgn(double a){return a < -eps ? -1 : a < eps ? 0 : 1;} using namespace std; LL f[maxn][maxn]; int root[maxn][maxn]; bool flag = true; inline LL find(int l,int r) { if (l > r) return 1; LL now; if (f[l][r] == -1) { for (int k = l;k <= r;k++) { now = find(l,k-1)*find(k+1,r) + f[k][k]; if (now > f[l][r]) { f[l][r] = now; root[l][r] = k; } } } return f[l][r]; } inline void pre(int l,int r) { if (l > r) return; if (flag) { flag = false; } else cout << " "; cout << root[l][r]; pre(l,root[l][r]-1); pre(root[l][r]+1,r); } int main() { ios::sync_with_stdio(false); int n; cin >> n; for (int i = 1;i <= n;i++) { for (int j = 1;j <= n;j++) f[i][j] = -1; } for (int i = 1;i <= n;i++) { cin >> f[i][i]; root[i][i] = i; } cout << find(1,n) << endl; flag = true; pre(1,n); return 0; }