NOIP2003加分二叉树提高组&&树形DP(区间)(java)

这篇博客主要介绍了如何解决一个关于二叉树的优化问题,根据中序遍历序列,通过区间DP或记忆化DFS计算树的最高加分,并输出前序遍历。博主详细分析了状态转移方程,解释了长度为1、2、3的状态,并提供了AC代码示例,包括常规DP和记忆化DFS两种解决方案。
摘要由CSDN通过智能技术生成

题目

题目链接: https://ac.nowcoder.com/acm/contest/19859/Q
题意:
 有n个节点的二叉树tree的中序遍历为(l,2,3,…,n)。一棵子树的加分(权值) = 他的 左子树的加分(权值)*右子树的加分(权值),叶子的加分(分数)就是叶节点本身的分数
要求输出:
1.tree的最高加分。
2.tree的前序遍历。


输入

5
5 7 1 2 10

输出

145
3 1 2 4 5


#解析 :
  *一,由于是中序遍历,所以我们可以考虑树形区间DP。求法类型与石子合拼(区间DP). 这里我利用了,子树的两种状态,1只有左子树或右子树(分割点在两端),2.左右子树都(分割点在中间),刚好对上了所有分割的点的位置(说明分析的状态一个不漏). 这里的状态对应的DP , 1.  dp[i][j] = dp[i][k-1]dp[]k+1[j]+dp[k][k],2. dp[i][k]+dp[k+1][j] 或 dp[i][k-1]+dp[k][j]。


  二,分析小状态,长度len为1,2,3的状态的解释。len=1, dp[][]=本身的值;len=2,状态2,只有一个子树;len=3时,1,2都有,一个for循环枚举分割点k即可。以此类推。


三,记录过程,即记录分割点的位置,我们不确定分割点的位置,所以我们需要记录所有的分割点,用vis[i][j]=t,表示区间[i,j]的分割点为t。当我们求出dp[1][n]的时候,分割的路线已经很明显了,递归打印分割点即可


#solution1, 常规DP
#solution2,记忆化DFS


AC代码 - - -

#solution1

import java.util.*;
import java.io.*;
public class Main {
    static int n,a[],vis[][],p=1;
    static boolean flag = true;
    static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    static int ini() throws IOException{
        int x=0,c=in.read();while(c<48||c>57){if(c==-1) return -1; c=in.read();}
        while(c>=48&&c<=57){x=(x<<3)+(x<<1)+(c^48);c=in.read();}return x;
    }
    static void dfs(int l,int r) {
        if(l>r||l<1||r>n) return ;
        int pos = vis[l][r];
        if(p!=n) {
            System.out.printf("%d ",pos);
        }else {
            System.out.printf("%d\n",pos);
        }
        p++;
        dfs(l,pos-1);
        dfs(pos+1,r);
    }
    public static void main(String[] args) throws IOException{
        n = ini();
        a = new int[n+2];
        vis = new int[n+2][n+2];
        int dp[][]=new int[n+2][n+2];
        for(int i = 1; i <= n; ++i) {
            a[i] = ini();
            dp[i][i] = a[i];
            vis[i][i] = i;
        }
        for(int l = 2; l <= n; ++l) {
            for(int i = 1; i <= n-l+1; ++i) {
                int j = i+l-1;
                int t = 0;
                for(int k = i; k <= j; ++k) {
                    if(k==i) {
                        t = dp[i][k]+dp[k+1][j];dp[i][k-1]+dp[k][j];
                    }else if(k==j){
                        t = dp[i][k-1]+dp[k][j];
                    }else {
                        t = dp[i][k-1]*dp[k+1][j]+dp[k][k];
                    }
                    if(t>dp[i][j]) {
                        dp[i][j] = t;
                        vis[i][j] = k;
                    }
                }
            }
        }
        System.out.printf("%d\n",dp[1][n]);
        dfs(1,n);
    }
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值