Luogu P1040 加分二叉树
这题想不出怎么dp于是看了眼题解……
是区间dpQwQ(看来什么前中后序遍历的概念还是有点不熟练)
因为中序遍历,左右儿子一定在根节点的左右。就是把树压扁了...(这个好像是企鹅学长说的qwq?)
f[i][j]表示区间i,j所能得到的最大值。最外层循环从小到大枚举长度。
f[i][j] = max(f[i][j],f[k][k] + f[i][k-1] * f[k+1][j])
当没有左/右儿子时(k == i或j),需要特判。
如何输出前序遍历?每次更新答案时,用root[i][j]记录当前区间的根节点。
最后从大到小的区间dfs一遍(根、左、右)即可。
代码如下
#include<cstdio> #include<iostream> #include<cmath> #include<cstring> #define MogeKo qwq using namespace std; const int maxn = 40; long long n,f[maxn][maxn],root[maxn][maxn]; void dfs(int l,int r){ if(l > r) return; printf("%lld ",root[l][r]); if(l == r) return; dfs(l,root[l][r]-1); dfs(root[l][r]+1,r); } int main() { scanf("%lld",&n); for(int i = 1; i <= n; i++){ scanf("%d",&f[i][i]); root[i][i] = i; } for(int len = 2; len <= n; len++) for(int i = 1; i <= n-1; i++) { int j = i+len-1; for(int k = i; k <= j; k++) if(k == i) { if(f[i][j] < f[k][k] + f[k+1][j]) { f[i][j] = f[k][k] + f[k+1][j]; root[i][j] = k; } } else if(k == j) { if(f[i][j] < f[k][k] + f[i][k-1]) { f[i][j] = f[k][k] + f[i][k-1]; root[i][j] = k; } } else { if(f[i][j] < f[k][k] + f[i][k-1]*f[k+1][j]) { f[i][j] = max(f[i][j],f[k][k] + f[i][k-1]*f[k+1][j]); root[i][j] = k; } } } printf("%lld\n",f[1][n]); dfs(1,n); return 0; }