解题思路
题目已经给出了树的中序遍历,因此我的想法是利用中序遍历的特点:若某子树的根结点为k,那么k之前的结点组成这一子树的左子树,k之后的结点组成这一子树的右子树,可以通过不断地枚举每个子树的根结点k,求出每个子树的最大加分:{ 左子树的最大加分*右子树的最大加分+ 根结点k的值}
以上是通过已知中序遍历想到是方法,结合已知条件,对于某一子树的中序遍历: {l, l + 1, ... , r} ,若根节点为k,那么 {l, l +1,...,k-1} 即为这一子树的左子树,{k+1,k+2,...,r}即为这一子树的右子树,因此,可以通过这种方法构造所有可能的树结构
根据上面的方法,我们通过递归求出每一段中序遍历{l,l+1,...,r}代表的子树的最大加分dp[l][r]以及根结点root[l][r],根据状态转移方程
dp[l][r] = max(dp[l][r],dp[l][k-1] + dp[k+1][r] + val[k]) { l <= k <= r}
并记录dp[l][r]取最大值时的根结点root[l][r],这样一来dp[1][n]即为我们所求的最大加分,又利用root和中序遍历求出前序遍历
代码区
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<queue> #include<string> #include<fstream> #include<vector> #include<stack> #include <map> #include <iomanip> #define bug cout << "**********" << endl #define show(x, y) cout<<"["<<x<<","<<y<<"] " #define LOCAL = 1; using namespace std; typedef long long ll; const ll inf = 1e18 + 7; const int mod = 1e9 + 7; const int Max = 1e5 + 10; int n; ll val[31]; ll dp[31][31]; //表示[l,r]子树的最大加分 int root[31][31]; //表示[l,r]子树的根结点 //dp,root均为以[l,r]组成的子树的数据 ll dfs(int l, int r) { if (l > r) //空树 return 1; if(l == r) //叶子节点 return dp[l][r] = val[l]; if (dp[l][r] != -1) { return dp[l][r]; } for (int k = l; k <= r; k++) //枚举子树[l,r]的根结点 { ll now = dfs(l,k-1) * dfs(k + 1, r) + val[k]; if(now > dp[l][r]) dp[l][r] = now,root[l][r] = k; } return dp[l][r]; } void dfs2(int l,int r) { if(l > r) //空树 return; printf("%d ",root[l][r]); dfs2(l,root[l][r] - 1); dfs2(root[l][r] + 1, r); } int main() { #ifdef LOCAL // freopen("input.txt", "r", stdin); // freopen("output.txt", "w", stdout); #endif scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%lld", val + i),root[i][i] = i; memset(dp,-1,sizeof(dp)); dfs(1,n); printf("%lld ",dp[1][n]); dfs2(1,n); printf(" "); return 0; }