0%

树形DP,洛谷P040

洛谷P1040/树形DP/区间DP

题目描述

设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第i个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数。
若某个子树为空,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree的最高加分
(2)tree的前序遍历

输入格式:

第1行:一个整数n(n<30),为节点个数。
第2行:n个用空格隔开的整数,为每个节点的分数(分数<100)。

输出格式:

第1行:一个整数,为最高加分(结果不会超过4,000,000,000)。
第2行:n个用空格隔开的整数,为该树的前序遍历。
输入输出样例

输入样例:

5
5 7 1 2 10

输出样例:

145
3 1 2 4 5

题目链接:洛谷P1040

题目解读

中序遍历为1,2,3,…..,那么当k为根节点时,编号小于k的节点必然在左子树上,编号大于k的节点必然在右子树上。对于一个节点,当他的值最大时,必然是他左右节点乘积最大时,那么有状态转移方程

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

遍历每一个节点当作根结点时的情况,再向左子树右子树分别遍历,当f[i][j]有值时直接返回值,没有值时找到最大值并返回(可能不是很好理解,具体见代码)。

同时,每次f[i][j]取得max时,记录root[i][j],也就是i节点和j节点的根节点,方便输出前序遍历。


更新一个区间DP的写法,思路都是一样的,区间DP先找出两个节点组成树的所有情况,再根据上面的状态转移方程,找出3个节点的情况,最后一步步推出所有节点的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
long long ans,f[31][31];
int n,root[31][31],cot=1;
long long dfs(int left,int right) //深搜,树形DP
{
long long k;
if(left>right) return 1;
if(f[left][right]==-1)
{
for(int i=left;i<=right;i++)
{
k=dfs(left,i-1)*dfs(i+1,right)+f[i][i];
if(k>f[left][right])
{
f[left][right]=k;
root[left][right]=i;
}
}
}
return f[left][right];
}
void pretra(int left,int right) //输出前序遍历
{
if(left>right) return;
if(cot++!=1)
cout<<' ';
cout<<root[left][right];
pretra(left,root[left][right]-1);
pretra(root[left][right]+1,right);
return ;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
f[i][j]=-1;
root[i][i]=i;
}
}
for(int i=1;i<=n;i++)
cin>>f[i][i];
cout<<dfs(1,n)<<endl;
pretra(1,n);
return 0;
}

区间DP的主要代码:

1
2
3
4
5
6
7
8
9
10
11
for(int len=2;len<=n;len++){             //最外层枚举长度
for(int i=1;i<=n-len+1;i++){ //第二层,枚举每一个长度为len的片段
int j=i+len-1;
for(int k=i;k<=j;k++){ //枚举某一个片段下每个节点当做更节点的情况
if(f[i][j]<f[i][k-1]*f[k+1][j]+f[k][k]){
f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];
root[i][j]=k;
}
}
}
}