动态规划,二叉树
本题要点:
1、状态表示:
dp[i][j] 表示 区间 [i,j] 的数作为二叉树,得到的最大得分
2、边界:
dp[i][i] = d[i]; // 只有一个点构成二叉树,最大得分就是该点的分值。
3、转态转移:
dp[i][j] = max{dp[k][k] + dp[i][k - 1] * dp[k + 1][j]}
区间[i,j] 构成的二叉树,假设以 k 点为二叉树的根(i <= k <= j), 扫描所有的根,看看谁的得分最大。
dp[1][n], 表示的就是 题目要求的最大总得分。
4、 root[i][j] 表示 [i,j] 构成的二叉树,谁是根节点。 题目要求输出,这颗二叉树的先序遍历顺序时候,
写个递归即可。
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int MaxN = 110;
int n;
int root[MaxN][MaxN]; // root[i][j] 表示 [i,j] 构成的二叉树,谁是根节点
long long d[MaxN];
long long dp[MaxN][MaxN];// dp[i][j] 表示 区间 [i,j] 的数作为二叉树,得到的得分
int ans[MaxN], cnt;
void inOrder(int i, int j)
{
if(i > j)
return;
if(i == j)
{
ans[++cnt] = root[i][j];
return;
}
ans[++cnt] = root[i][j];
inOrder(i, root[i][j] - 1);
inOrder(root[i][j] + 1, j);
}
void solve()
{
for(int i = 1; i <= n; ++i)
for(int j = 1; j < i; ++j)
dp[i][j] = 1;
for(int i = 1; i <= n; ++i)
{
dp[i][i] = d[i];
root[i][i] = i; // 第i 号节点是根节点
if(i + 1 <= n)
dp[i][i + 1] = d[i] + d[i + 1], root[i][i + 1] = i;
}
long long tmp;
for(int step = 2; step < n; ++step)
{
for(int i = 1; i + step <= n; ++i)
{
int j = i + step;
for(int k = i; k <= j; ++k) // 以k为根的二叉树,计算得分
{
tmp = dp[k][k] + dp[i][k - 1] * dp[k + 1][j];
if(tmp > dp[i][j])
{
dp[i][j] = tmp;
root[i][j] = k;
}
}
}
}
printf("%lld\n", dp[1][n]);
inOrder(1, n);
for(int i = 1; i <= n; ++i)
{
printf("%d", ans[i]);
if(i + 1 <= n)
printf(" ");
else
printf("\n");
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%lld", &d[i]);
solve();
return 0;
}
/*
5
5 7 1 2 10
*/
/*
145
3 1 2 4 5
*/