noip 2003 加分二叉树(DP)
思路:
用一个二维数组dp[i, j]表示中序遍历中从 i 到 j 的区间组成的子树的集合,dp[i, j]的值表示这些子树中得分的最大值,转移方程很简单,dp[i, j] = max(dp[i, j], dp[i, k - 1] + dp[k + 1, r],需要遍历 i 到 j 的每一个点,判断哪一个是根节点,然后根据根节点把树再次分为左右子树
注意计算顺序,为了保证计算当前值的时候需要用到的值已经被计算过了,需要对区间的长度进行遍历,区间长度从短到长计算,其次遍历起点位置,这样可以保证在计算dp[i, j]时所需要的dp[i, k - 1]出现过(显而易见,k - 1 在 j 左侧),也保证dp[k + 1, j]出现过(也很显然,因为长度小于当前长度),对于输出前序遍历,只需要在每次更新的时候记录当前的根节点是哪一个点即可,输出的时候先输出整个区间的根,然后依次往左往右递归即可
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
int n;
ll points[33], dp[33][33], root[33][33];
void out(int l, int r){
if(l > r)return;
int k = root[l][r];
printf("%d ", k);
out(l, k - 1);
out(k + 1, r);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++){
scanf("%lld", &points[i]);
}
for (int len = 1; len <= n; len++){
for (int l = 1; l + len - 1 <= n; l++){
int r = l + len - 1;
for (int k = l; k <= r; k++){
ll lef, rig, s;
if(l == k){
lef = 1;
}
else lef = dp[l][k - 1];
if(k == r)rig = 1;
else rig = dp[k + 1][r];
s = rig * lef + points[k];
if(l == r)s = points[k];
if(dp[l][r] < s){
dp[l][r] = s;
root[l][r] = k;
}
}
}
}
printf("%d\n", dp[1][n]);
out(1, n);
return 0;
}
本文来自博客园,作者:correct,转载请注明原文链接:https://www.cnblogs.com/correct/p/12862059.html

浙公网安备 33010602011771号