1. 程式人生 > >luogu1040 加分二叉樹

luogu1040 加分二叉樹

遍歷 整數 esp oid class nod val rgs str

題目大意

設一個n個節點的二叉樹tree的中序遍歷為(l,2,3,…,n),其中數字1,2,3,…,n為節點編號。每個節點都有一個分數(均為正整數),記第j個節點的分數為ditree及它的每個子樹都有一個加分,任一棵子樹subtree(也包含tree本身)的加分計算方法如下:

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

若某個子樹為主,規定其加分為1,葉子的加分就是葉節點本身的分數。不考慮它的空子樹。

試求一棵符合中序遍歷為(1,2,3,…,n)且加分最高的二叉樹tree。要求輸出;

1tree的最高加分

2tree的前序遍歷

題解

  本題最容易忽略的性質便是二叉樹中的每一個子樹的中序遍歷都是一段連續的區間。所以對於一段區間,根據選區間中哪個點作為根來分類動規即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdarg>
using namespace std;

void _printf(char *format, ...)
{
#ifdef _DEBUG
    va_list(args);
    va_start(args, format);
    vprintf(format, args);
    va_end(args);
#endif
}
//-------------------------------------------------------------------------
const int MAX_NODE = 35;
long long F[MAX_NODE][MAX_NODE];
int RootId[MAX_NODE][MAX_NODE], Val[MAX_NODE];
int TotNode;

void DP()
{
    for (int i = 1; i <= TotNode; i++)
    F[i][i - 1] = F[i][i + 1] = 1;
    for (int i = 1; i <= TotNode; i++)
    {
    F[i][i] = Val[i];
    RootId[i][i] = i;
    }
    for (int len = 2; len <= TotNode; len++)
    for (int i = 1; i <= TotNode - len + 1; i++)
    {
        int j = i + len - 1;
        for (int k = i; k <= j; k++)
        {
        if (F[i][k - 1] * F[k + 1][j] + Val[k] > F[i][j])
        {
            F[i][j] = F[i][k - 1] * F[k + 1][j] + Val[k];
            RootId[i][j] = k;
        }
        }
    }
}

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

int main()
{
#ifdef _DEBUG
    freopen("c:\\noi\\source\\input.txt", "r", stdin);
#endif
    scanf("%d", &TotNode);
    for (int i = 1; i <= TotNode; i++)
    scanf("%d", Val + i);
    DP();
    printf("%lld\n", F[1][TotNode]);
    Print(1, TotNode);
    return 0;
}

  

luogu1040 加分二叉樹