1130. 叶值的最小代价生成树

给定一个正整数数组,构建满足特定条件的二叉树,其中非叶节点的值等于其子叶节点最大值的乘积,目标是最小化所有非叶节点的值之和。该问题可以通过动态规划解决,通过计算区间内最大值和非叶节点的最优组合来找到最小总和。示例中,对于数组 [6,2,4],最小总和为 32。
摘要由CSDN通过智能技术生成

给你一个正整数数组 arr,考虑所有满足以下条件的二叉树:

每个节点都有 0 个或是 2 个子节点。
数组 arr 中的值与树的中序遍历中每个叶节点的值一一对应。(知识回顾:如果一个节点有 0 个子节点,那么该节点为叶节点。)
每个非叶节点的值等于其左子树和右子树中叶节点的最大值的乘积。
在所有这样的二叉树中,返回每个非叶节点的值的最小可能总和。这个和的值是一个 32 位整数。

示例:

输入:arr = [6,2,4]
输出:32
解释:
有两种可能的树,第一种的非叶节点的总和为 36,第二种非叶节点的总和为 32。

    24            24
   /  \          /  \
  12   4        6    8
 /  \               / \
6    2             2   4
 

提示:

2 <= arr.length <= 40
1 <= arr[i] <= 15
答案保证是一个 32 位带符号整数,即小于 2^31。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

最大的最小值(最小的最大值)问题
先求max矩阵:表示arr数组,i到j内的最大值

dp[i][j]表示i到j区间内的非叶子结点的最小总和

对于i、j 相邻j = i + 1,显然dp[i][j] = arr[i] * arr[j](需要预处理)
对于相邻三个节点 例如dp[0][3]

                下标:    0   1.  2
假如三个数分别是 1, 2, 3
那么可以组合的情况是 (1, 2), 3 | 1,(2, 3)
那么dp[0][3] = min(dp[0][1] + arr[2] * max[0][1],  arr[0] * max[1][2] + dp[1][2])
对于更多的节点

作者:zwhyS7
链接:https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/solution/golang-by-zwhys7-p5fd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

	    下标:0, 1, 2, 3, 4 
例如五个节点 1, 2, 3, 4, 5
先处理边界分类的情况:1 (2, 3, 4, 5); (1, 2, 3, 4), 5
	这样可以避免 从i 到j 之间劈开进行讨论的时候,如果出现dp[0][3] 和dp[4][4]的情况,避免k = j - 1
对于其他分类情况,比如 (1, 2, 3) (4, 5) // i = 0, j = 4, k 属于i + 1 -> j - 2
dp[0][4] = min(dp[0][4], dp[0][2] /*左侧非页节点之和 */ + dp[3][4] /*右侧非叶子之和*/ + max[0][2] * max[3][4] /*跟节点的值*/)

作者:zwhyS7
链接:https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/solution/golang-by-zwhys7-p5fd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
func mctFromLeafValues(arr []int) int {
    n := len(arr)
    dp, max := make([][]int, n), make([][]int, n)
    for i := 0; i < n; i++{
        dp[i] = make([]int, n)
        max[i] = make([]int, n)
        max[i][i] = arr[i]
    }
    for i := 0; i < n; i++{
        cur := arr[i]
        for j := i + 1; j < n; j++{
            if arr[j] > cur{
                cur = arr[j]
            }
            max[i][j] = cur
        }
    }

    for i := 1; i < n; i++{
        dp[i - 1][i] = arr[i - 1] * arr[i]
    }
    for i := n - 3; i >= 0; i--{ // 单个节点n - 1没意义跳过,n - 2 到 n - 1属于相邻的两个节点,已经预处理了
        for j := i + 2; j < n; j++{ // j从i + 2开始,因为i + 1属于相邻的节点 预处理了已经
          // 这里,见 5
            dp[i][j] = min(arr[i] * max[i + 1][j] + dp[i + 1][j], dp[i][j - 1] + max[i][j - 1] * arr[j])
            if j == i + 2{ // 三个相邻的话,上一行代码就已经处理完了,不需要走下面
                continue
            }
          // k 
            for k := i + 1; k < j - 1; k++{
                dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + max[i][k] * max[k + 1][j])
            }
        }
    }
    return dp[0][n - 1]
}

func min(x, y int)int{
    if x <= y{
        return x
    }
    return y
}

作者:zwhyS7
链接:https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/solution/golang-by-zwhys7-p5fd/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值