Luogu P1040 加分二叉树

gate

这题想不出怎么dp于是看了眼题解……

是区间dpQwQ(看来什么前中后序遍历的概念还是有点不熟练)

因为中序遍历,左右儿子一定在根节点的左右。就是把树压扁了...(这个好像是企鹅学长说的qwq?)

f[i][j]表示区间i,j所能得到的最大值。最外层循环从小到大枚举长度。

f[i][j] = max(f[i][j],f[k][k] + f[i][k-1] * f[k+1][j])

当没有左/右儿子时(k == i或j),需要特判。

如何输出前序遍历?每次更新答案时,用root[i][j]记录当前区间的根节点。

最后从大到小的区间dfs一遍(根、左、右)即可。

代码如下

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#define MogeKo qwq
using namespace std;

const int maxn = 40;
long long n,f[maxn][maxn],root[maxn][maxn];

void dfs(int l,int r){
    if(l > r) return;
    printf("%lld ",root[l][r]);
    if(l == r) return;
    dfs(l,root[l][r]-1);
    dfs(root[l][r]+1,r);
}

int main() {
    scanf("%lld",&n);
    for(int i = 1; i <= n; i++){
        scanf("%d",&f[i][i]);
        root[i][i] = i;
    }
    for(int len = 2; len <= n; len++)
        for(int i = 1; i <= n-1; i++) {
            int j = i+len-1;
            for(int k = i; k <= j; k++)
                if(k == i) {
                    if(f[i][j] < f[k][k] + f[k+1][j]) {
                        f[i][j] = f[k][k] + f[k+1][j];
                        root[i][j] = k;
                    }
                } else if(k == j) {
                    if(f[i][j] < f[k][k] + f[i][k-1]) {
                        f[i][j] = f[k][k] + f[i][k-1];
                        root[i][j] = k;
                    }
                } else {
                    if(f[i][j] < f[k][k] + f[i][k-1]*f[k+1][j]) {
                        f[i][j] = max(f[i][j],f[k][k] + f[i][k-1]*f[k+1][j]);
                        root[i][j] = k;
                    }
                }
        }
    printf("%lld
",f[1][n]);
    dfs(1,n);
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/mogeko/p/11632360.html