这道题是典型的区间DP(动态规划)问题,核心在于如何高效计算每个子数组的"重要性"。

问题分析

重要性的计算规则:
- 子数组中只出现一次的数字会被移除(不计入长度)
- 重要性 = k + 剩余数字的个数
- 等价于:重要性 = k + (子数组长度 - 只出现一次的数字个数)

关键观察:
- 某个数字在子数组中出现1次 → 不贡献长度
- 出现2次及以上 → 全部贡献长度(包括第1次)

动态规划思路

定义 dp[i] 表示拆分前 i 个元素(下标 0 到 i-1)的最小代价。

状态转移:
dp[i] = min(dp[j] + cost(j, i-1))  其中 0 ≤ j < i

- dp[0] = 0(空数组代价为0)
- cost(l, r) 表示子数组 nums[l..r] 的重要性

Java实现

class Solution {
    public int minCost(int[] nums, int k) {
        int n = nums.length;
        int[] dp = new int[n + 1];
        // 初始化dp数组为较大值
        for (int i = 1; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        dp[0] = 0;
        
        // 枚举每个结束位置
        for (int i = 1; i <= n; i++) {
            // 统计当前子数组中每个数字的出现次数
            int[] count = new int[n]; // nums[i] < nums.length
            int uniqueCount = 0; // 只出现一次的数字个数
            
            // 从后往前枚举子数组起点
            for (int j = i - 1; j >= 0; j--) {
                int val = nums[j];
                count[val]++;
                
                if (count[val] == 1) {
                    // 首次出现,算作"唯一"数字
                    uniqueCount++;
                } else if (count[val] == 2) {
                    // 第二次出现,不再唯一
                    uniqueCount--;
                }
                // 出现3次及以上,不影响uniqueCount
                
                // 子数组长度 = i - j
                int subLen = i - j;
                // 重要性 = k + (子数组长度 - 唯一数字个数)
                int importance = k + (subLen - uniqueCount);
                
                // 更新dp[i]
                dp[i] = Math.min(dp[i], dp[j] + importance);
            }
        }
        
        return dp[n];
    }
}

代码优化(使用HashMap处理任意数值范围)

如果 nums[i] 的范围不固定,可以用 HashMap 替代数组:

class Solution {
    public int minCost(int[] nums, int k) {
        int n = nums.length;
        int[] dp = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            dp[i] = Integer.MAX_VALUE;
        }
        dp[0] = 0;
        
        for (int i = 1; i <= n; i++) {
            Map<Integer, Integer> freq = new HashMap<>();
            int uniqueCount = 0;
            
            for (int j = i - 1; j >= 0; j--) {
                int val = nums[j];
                int count = freq.getOrDefault(val, 0) + 1;
                freq.put(val, count);
                
                if (count == 1) {
                    uniqueCount++;
                } else if (count == 2) {
                    uniqueCount--;
                }
                
                int subLen = i - j;
                int importance = k + (subLen - uniqueCount);
                dp[i] = Math.min(dp[i], dp[j] + importance);
            }
        }
        
        return dp[n];
    }
}

复杂度分析

- 时间复杂度:O(n²),n ≤ 1000,完全可行
- 空间复杂度:O(n)

示例验证

以 nums = [1,2,1,2,1,3,3], k = 2 为例:
- 最优拆分:[1,2] + [1,2,1,3,3]
- 代价 = (2 + 0) + (2 + 4) = 8 ✅

这个解法清晰高效,能通过LeetCode的所有测试用例。′

 

更多推荐