题目链接:P1040
个人认为一个非常恶心的题,卡了我一天半。。。蒟蒻表示毫无办法,后面根据题解写出来了,现在总结一下
注意:千万不要自己去手写二叉树,这个题没必要真正的把树建起来,去建树的不是傻逼吗。我最开始自己去建树了
首先题目要求我们必须保证该树的中序遍历序列为12345~n,中序序列有一个特点,就是他左边的结点是他的左子树,他右边的结点是他的右子树,这有什么用呢?(我们就把题目所给的结点看成几个点,不用想办法给他画成树,我们只需要找到各结点中适合作为根节点的点把他存起来就好了,循环找出这个结点,这个结点左边的就是他的左子树,右边的就是他的右子树)
我们用root[][]这个二维数组来存 例如:root[1][7] 表示从第一个结点到第7个结点最适合作为根结点的编号就是root[1][7]
我们要根据什么找这个结点是不是该几个结点中最适合作为跟结点的呢,看题目另外个条件,题目要我们求出加分最大的那个树,而该树的加分为(左子树分值*右子树分值+根结点分值),所以我们只需要根据这个就可以找到最适合作为根结点的编号了(此处又出现了一个问题,我们在求左子树分值跟右子树分值的时候我们必然又会跳到另外一个阶段来求另外几个结点最适合作为根结点的编号) 这里我们就会用到一个记忆化搜索的方法,记忆化搜索的意思很简单,就是如果这个我搜索过了,我就给他赋个值,下次搜索的时候如果该地方有值了就表示我搜索过了,就跳过就好了
有问题欢迎评论,写的好像太啰嗦了。。
代码:
import java.util.*;
public class Main {
static long dp[][];
static int root[][];
static int n;
static long search(int x,int y){
if(x == y){
root[x][x] = x;
return dp[x][x];
}
if(x > y)
return 1;
if(dp[x][y] > 0) // 记忆化搜索 如果大于0说明x~y已经搜索过最适合作为根结点的是哪个了
return dp[x][y];
for(int i = x;i <= y;i++){ // 找到使结点x到y 分最大的根节点 记录在root中
long p=search(x,i-1)*search(i+1,y)+dp[i][i]; // 以i作为根结点的分值
if(p > dp[x][y]){
dp[x][y] = p;
root[x][y] = i;
}
}
return dp[x][y];
}
static void print(int x,int y){
if(x > y)
return;
System.out.print(root[x][y]+" ");
print(x,root[x][y]-1);
print(root[x][y]+1,y);
}
public static void main(String args[]){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
dp = new long[n+1][n+1];
root = new int[n+1][n+1];
for(int i = 1;i <= n;i++)
dp[i][i] = sc.nextLong();
System.out.println(search(1,n));
print(1,n);
}
}