@
题目
https://www.luogu.com.cn/problem/P1040
分析
这题被分到树形DP的题单上,但是个人感觉更像区间DP+上树的遍历.
看透了这题的情况下这题应该不难,关键就在“中序遍历为(1,2,3,...n)”,这就意味着一段连续的区间[l,r],以root为根节点,那么左子树就由[l,root-1]组成,右子树由[root+1,r]组成(联想下二叉查找树),这样,状态转移方程就很好推了。
设f[i][j]表示以区间[l,r]为一棵(子)树的能达到的最大加分,我们就可以枚举这棵树的根节点k,则有:
[f[i][j]=max {^{f[i][k-1]*f[k+1][j]+score[k] (i<k<j)(以中间点为根)}_{max{^{f[i+1][j] + score[i]}_{f[i][j-1]+sorce[j]}(以左右端点为根)}
]
特别地,当i=j时f[i][j]=socre[i] (题目有讲)
以区间长度为状态,区间起点为阶段,这不就是区间DP吗?
================== 分割线 ==================
一轮DP下来,我们就知道了二叉树的最高加分,接下来就是遍历了:
DP的状态转移中,我们枚举(子)树的根节点,要是我们把它记录下来,那不就能输出前序遍历了吗?
设root[i][j]为区间[l,r]产生最高加分的根节点,并在状态转移的时候记录,根据root数组,就能写出DFS求前序遍历(注意边界等细节):
void dfs(int l , int r){//前序遍历
if(l > r)return;
if(l == r){
cout << l << ' ';
return;
}
int k = root[l][r];
cout << k << ' ';
dfs(l , k - 1);
dfs(k + 1 , r);
}
难道就这么轻松愉快地AC了?
NONONO,还有一个小细节:字典序输出(题目没有明说)
这就要求我们在写DP的时候注意一下枚举k点的顺序(要严格按字典序枚举)
f[j][r] = f[j + 1][r] + score[j] , root[j][r] = j;
for(int k = j + 1 ; k < r ; k++){
if(f[j][r] < f[j][k - 1] * f[k + 1][r] + score[k])
f[j][r] = f[j][k - 1] * f[k + 1][r] + score[k] , root[j][r] = k;
}
if(f[j][r] < f[j][r - 1] + score[r])
f[j][r] = f[j][r - 1] + score[r] , root[j][r] = r;
即:先枚举k为左端点的情况,再中间,最后右端点(千万不要贪方便把左右端点合并了我一开始就是这样)
然后:
真·AC
撒花
代码
#include <iostream>
#include <cstdio>
using namespace std;
int n;
int score[50];
int f[50][50];
int root[50][50];
void dfs(int l , int r){//前序遍历
if(l > r)return;
if(l == r){
cout << l << ' ';
return;
}
int k = root[l][r];
cout << k << ' ';
dfs(l , k - 1);
dfs(k + 1 , r);
}
int main(){
cin >> n;
for(int i = 1 ; i <= n ; i++)
cin >> score[i];
//求最大分数
for(int i = 1 ; i <= n ; i++)
f[i][i] = score[i] , root[i][i] = i;
for(int i = 2 ; i <= n ; i++)
for(int j = 1 ; j <= n ; j++){
int r = i + j - 1;
f[j][r] = f[j + 1][r] + score[j] , root[j][r] = j;
for(int k = j + 1 ; k < r ; k++){
if(f[j][r] < f[j][k - 1] * f[k + 1][r] + score[k])
f[j][r] = f[j][k - 1] * f[k + 1][r] + score[k] , root[j][r] = k;
}
if(f[j][r] < f[j][r - 1] + score[r])
f[j][r] = f[j][r - 1] + score[r] , root[j][r] = r;
}
cout << f[1][n] << endl;
dfs(1 , n);
cout << endl;
return 0;
}