加分二叉树

传送门

又是熟悉的遍历题……好了又不会了。

这是一道很神奇的题,虽然可以称为树形DP但是它不需要建树!

首先,因为中序遍历每个连续的一段都对应一棵子树,所以我们完全可以使用区间DP的方法去把小区间合并成大区间来计算。

使用dp[i][j]表示区间i,j之内(也就是一棵子树)的最高加分。特殊的,dp[i][i-1]表示一棵空子树,其权值为1。

这样的话转移方程就是dp[i][j] = max(dp[i][j],dp[i][k-1] * dp[i][k+1] + dp[k][k]),如果这个值较大发生了更新,就直接把它设置为这段区间的根节点。

这样我们求成功求出了最大值。至于前序遍历很好办,我们递归访问就可以了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<set>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')

using namespace std;
typedef long long ll;
const int M = 1000005;
const int INF = 1e9+7;

int read()
{
    int ans = 0,op = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
    {
        if(ch == '-') op = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
    {
        ans *= 10;
        ans += ch - '0';
        ch = getchar();
    }
    return ans * op;
}

int dp[100][100],v[100],n,root[105][105];

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

int main()
{
    n = read();
    rep(i,1,n) v[i] = read();
    rep(i,1,n) dp[i][i] = v[i],dp[i][i-1] = 1;
    per(i,n,1)
    rep(j,i+1,n)
    rep(k,i,j)
    {
        if(dp[i][j] < (dp[i][k-1] * dp[k+1][j] + dp[k][k]))
        dp[i][j] = dp[i][k-1] * dp[k+1][j] + dp[k][k],root[i][j] = k;    
    }
    printf("%d\n",dp[1][n]);
    print(1,n);
    return 0;
}

 

posted @ 2018-09-12 20:24  CaptainLi  阅读(111)  评论(0编辑  收藏  举报