leetcode
leetcode 2851 ~ 2900
切分数组

切分数组

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 361 ms, 内存: 38.0 MB


/*
题目思路:
1. 使用Java Stream API遍历数组并找到每个可能的切割点,计算其前后子数组的最大公约数。
2. 如果某个切割点的前后子数组的头尾最大公约数都大于1,则可以切割。
3. 记录最少的切割次数。
*/
import java.util.stream.IntStream;

public class Solution {
    // 计算两个数的最大公约数
    private int gcd(int a, int b) {
        if (b == 0) return a;
        return gcd(b, a % b);
    }

    public int minCuts(int[] nums) {
        int n = nums.length;
        int[] cuts = {0};

        IntStream.range(0, n).forEach(i -> {
            if (i > 0 && gcd(nums[i - 1], nums[i]) == 1) {
                cuts[0]++;
            }
        });
        return cuts[0] + 1;
    }
}

解释

方法:

本题解采用动态规划的思想以及预处理的方法来解决问题。首先,通过一个预处理步骤,使用筛法计算每个数字的最小质因子。这个预处理步骤是基于一个修改版的埃拉托斯特尼筛法,只针对较小的质数。对于数组中的每个数字,我们使用预处理的结果来快速找到它的所有质因子,并且通过动态规划的方式计算如何分割数组以满足题目要求。动态规划的状态 f[p] 表示最后一个使用质数 p 作为头尾最大公约数的子数组所形成的最小切割数。对于每个数字 x,我们更新它的所有质因子对应的状态,然后找出最小的切割数作为当前位置的结果,并不断向前推进。

时间复杂度:

O(n log x)

空间复杂度:

O(M)

代码细节讲解

🦆
在题解中提到的埃拉托斯特尼筛法是如何被修改的?具体修改了哪些部分?
题解中所提到的埃拉托斯特尼筛法的修改主要集中在不是标记所有的合数,而是只标记每个合数的最小质因子。这种修改使得在后续处理中,我们可以快速获取任何一个数的最小质因子,而不需要经过复杂的因式分解。具体来说,对于每个质数p,我们从p*p开始(因为小于p*p的合数已经被更小的质数标记过了),以p为步长遍历,将这些位置上的数标记为p。这样,当我们查询一个数的最小质因子时,可以直接得到,从而加速了解题过程中对质因子的查询。
🦆
题解中使用了动态规划,具体的状态定义是什么?状态转移方程是如何根据题目要求构建的?
题解中使用的动态规划的状态定义为f[p],这表示以质数p为头尾的子数组的最小切割数。状态转移方程是基于每个数字x的质因子来更新的。具体地,对于每个数字x,我们遍历其所有质因子,并尝试更新这个质因子对应的状态f[p]。更新规则是:f[p] = min(f.get(p, 100001), pre + 1),其中pre是上一个位置的最小切割数。这样,通过不断更新各个质因子对应的状态,我们可以计算出到当前数字为止的最小切割数。
🦆
为什么题解选择只计算小于1000的质数?处理大于1000的数时策略有什么不同?
题解中选择只计算小于1000的质数是因为,在大多数情况下,较小的质数是组成数的主要质因子,而大于1000的质数较少出现作为因子,特别是在考虑到数字范围常常远大于1000的情况下。处理大于1000的质因子时,题解采用的策略是直接检查剩余的数x是否大于1,如果是,那么这个数本身是一个质数,因此直接更新f[x] = min(f.get(x, 100001), pre + 1)。这种处理方式确保了即使是大于1000的质数也能被正确地处理,而不需要额外的复杂筛法。

相关问题