[HOT 100] 1000. 合并石头的最低成本

1. 题目链接


1000. 合并石头的最低成本 - 力扣(LeetCode)


2. 题目描述


n 堆石头排成一排,第 i 堆中有 stones[i] 块石头。

每次 移动 需要将 连续的 k 堆石头合并为一堆,而这次移动的成本为这 k 堆中石头的总数。

返回把所有石头合并成一堆的最低成本。如果无法合并成一堆,返回 -1


3. 题目示例


示例 1 :

输入:stones = [3,2,4,1], K = 2
输出:20
解释:
从 [3, 2, 4, 1] 开始。
合并 [3, 2],成本为 5,剩下 [5, 4, 1]。
合并 [4, 1],成本为 5,剩下 [5, 5]。
合并 [5, 5],成本为 10,剩下 [10]。
总成本 20,这是可能的最小值。

示例 2 :

输入:stones = [3,2,4,1], K = 3
输出:-1
解释:任何合并操作后,都会剩下 2 堆,我们无法再进行合并。所以这项任务是不可能完成的。

示例 3 :

输入:stones = [3,5,1,2,6], K = 3
输出:25
解释:
从 [3, 5, 1, 2, 6] 开始。
合并 [5, 1, 2],成本为 8,剩下 [3, 8, 6]。
合并 [3, 8, 6],成本为 17,剩下 [17]。
总成本 25,这是可能的最小值。

4. 解题思路


  1. 问题理解
    • 给定n堆石头,每次合并连续的k堆,成本为这些石头的总和
    • 目标是将所有石头合并成一堆的最小总成本
    • 如果不能合并成一堆,返回-1
  2. 关键思路
    • 检查可行性:(n-1)必须能被(k-1)整除
    • 使用前缀和数组快速计算区间和
    • 记忆化搜索避免重复计算
    • 分治思想:将大问题分解为子问题
  3. 动态规划设计
    • 状态定义:dfs(i,j)表示合并区间[i,j]的最小成本
    • 状态转移:尝试所有可能的分割点m(步长k-1)
    • 合并条件:当区间长度减1能被k-1整除时才能合并
  4. 计算顺序
    • 自顶向下递归计算
    • 使用记忆化存储中间结果
    • 优先处理小区间,逐步扩大

5. 题解代码


class Solution {
    private int[][] memo;  // 记忆化数组,存储已计算过的区间结果
    private int[] s;       // 前缀和数组
    private int k;         // 每次合并的堆数

    public int mergeStones(int[] stones, int k) {
        int n = stones.length;
        // 检查是否可以合并成一堆:(n-1)必须能被(k-1)整除
        if ((n - 1) % (k - 1) > 0)
            return -1;

        // 初始化前缀和数组
        s = new int[n + 1];
        for (int i = 0; i < n; i++)
            s[i + 1] = s[i] + stones[i]; // s[i]表示前i个元素的和

        this.k = k;
        memo = new int[n][n];
        // 初始化记忆数组为-1,表示未计算
        for (int i = 0; i < n; ++i)
            Arrays.fill(memo[i], -1);

        // 从整个数组范围开始递归计算
        return dfs(0, n - 1);
    }

    private int dfs(int i, int j) {
        // 基本情况:只有一个堆,无需合并
        if (i == j) return 0;
        
        // 如果已经计算过,直接返回结果
        if (memo[i][j] != -1) return memo[i][j];
        
        int res = Integer.MAX_VALUE;
        // 尝试所有可能的分割点m(步长为k-1)
        for (int m = i; m < j; m += k - 1)
            res = Math.min(res, dfs(i, m) + dfs(m + 1, j));
        
        // 如果当前区间可以合并成一堆,加上合并成本
        if ((j - i) % (k - 1) == 0)
            res += s[j + 1] - s[i];
        
        // 存储结果并返回
        return memo[i][j] = res;
    }
}


6. 复杂度分析


时间复杂度:O(n³/k)

  • 递归树深度:O(n/k)
  • 每层计算量:O(n²)
  • 记忆化确保每个状态只计算一次

空间复杂度:O(n²)

  • 记忆化数组memo[n][n]
  • 前缀和数组s[n+1]
  • 递归调用栈深度O(n)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值