原题链接:https://www.luogu.org/problem/show?pid=1040
刚拿到这道题的我是懵逼的,因为刚开始除了暴力我完全想不到从哪入手。
其实这题是一个区间dp和dfs,代码还是挺简单的。
参考了一篇题解,对那位大佬表示由衷的敬意。
设f[i][j]表示在区间(i,j)内的最大加分,那么有f[i][j] = max(f[i][k-1]*f[k+1][j]+a[k]) ,a[k]代表第i个点的分数,k是枚举的根节点,即状态转移方程的那个式子是指的在区间(i,j)上的以k为根节点的最大分数。
还记得区间dp的基本思路吗?由小区间推出大区间,这题也是这样,i从n倒推至1,j从i正推至n,而中间的枚举根节点k的范围是[i,j]。
同时我们还需要一个数组n[i][j]表示在区间(i,j)加分最大时的根节点,在后面会用到。
最高加分便是f[1][N]。//为了不和n数组重复用一个大写
考虑下一个问题,如何输出一棵树的前序遍历?
还记得什么是前序遍历吗?先根,再左子树,再右子树。
要输出一个遍历,递归输出就好。首先输出当前的点(根节点),然后输出左子树,再输出右子树,简单的划分。
参考代码:
1 #include <iostream> 2 #define maxn 35 3 using namespace std; 4 int a[maxn]; 5 int N; 6 int f[maxn][maxn]; 7 int n[maxn][maxn]; 8 9 10 void dfs(int l,int r){ 11 if (l>r) 12 return; 13 int mid = n[l][r]; 14 cout << mid << " "; 15 dfs(l,mid-1); 16 dfs(mid+1,r); 17 } 18 19 int main(){ 20 cin >> N; 21 for (int i=0;i<=N;i++) 22 for (int j=0;j<=N;j++) 23 f[i][j] = 1; 24 25 for (int i=1;i<=N;i++){ 26 cin >> a[i]; 27 f[i][i] = a[i]; 28 n[i][i] = i; 29 } 30 31 for (int i=N-1;i>=1;i--) 32 for (int j=i+1;j<=N;j++) 33 for (int k=i;k<=j;k++) 34 if (f[i][k-1]*f[k+1][j]+a[k]>f[i][j]){ 35 n[i][j]=k; 36 f[i][j]=f[i][k-1]*f[k+1][j]+a[k]; 37 } 38 cout << f[1][N] << endl; 39 dfs(1,N); 40 41 return 0; 42 }