题目描述
给定一个整数数组 nums
。
如果要将整数数组 nums
拆分为 子数组 后是 有效的,则必须满足:
- 每个子数组的第一个和最后一个元素的最大公约数 大于
1
,且
nums
的每个元素只属于一个子数组。
返回 nums
的 有效 子数组拆分中的 最少 子数组数目。如果不能进行有效的子数组拆分,则返回 -1
。
注意:
- 两个数的 最大公约数 是能整除两个数的最大正整数。
- 子数组 是数组中连续的非空部分。
示例 1:
输入: nums = [2,6,3,4,3]
输出: 2
解释: 我们可以通过以下方式创建一个有效的分割: [2,6] | [3,4,3].
- 第一个子数组的起始元素是 2,结束元素是 6。它们的最大公约数是 2,大于 1。
- 第二个子数组的起始元素是 3,结束元素是 3。它们的最大公约数是 3,大于 1。
可以证明,2 是我们在有效分割中可以获得的最少子数组数。
示例 2:
输入: nums = [3,5]
输出: 2
解释: 我们可以通过以下方式创建一个有效的分割: [3] | [5].
- 第一个子数组的起始元素是 3,结束元素是 3。它们的最大公约数是 3,大于 1。
- 第二个子数组的起始元素是 5,结束元素是 5。它们的最大公约数是 5,大于 1。
可以证明,2 是我们在有效分割中可以获得的最少子数组数。
示例 3:
输入: nums = [1,2,1]
输出: -1
解释: 不可能创建有效的分割。
提示:
1 <= nums.length <= 1000
1 <= nums[i] <= 105
解法
方法一:记忆化搜索
我们设计一个函数 $dfs(i)$ 表示从下标 $i$ 开始的最小分割数。对于下标 $i$,我们可以枚举所有的分割点 $j$,即 $i \leq j \lt n$,其中 $n$ 为数组长度。对于每个分割点 $j$,我们需要判断 $nums[i]$ 和 $nums[j]$ 的最大公约数是否大于 $1$,如果大于 $1$,则可以进行分割,此时分割数为 $1 + dfs(j + 1)$,否则分割数为 $+\infty$。最后我们取所有分割数的最小值即可。
时间复杂度 $O(n^2)$,空间复杂度 $O(n)$。其中 $n$ 为数组长度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | class Solution:
def validSubarraySplit(self, nums: List[int]) -> int:
@cache
def dfs(i):
if i >= n:
return 0
ans = inf
for j in range(i, n):
if gcd(nums[i], nums[j]) > 1:
ans = min(ans, 1 + dfs(j + 1))
return ans
n = len(nums)
ans = dfs(0)
dfs.cache_clear()
return ans if ans < inf else -1
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 | class Solution {
private int n;
private int[] f;
private int[] nums;
private int inf = 0x3f3f3f3f;
public int validSubarraySplit(int[] nums) {
n = nums.length;
f = new int[n];
this.nums = nums;
int ans = dfs(0);
return ans < inf ? ans : -1;
}
private int dfs(int i) {
if (i >= n) {
return 0;
}
if (f[i] > 0) {
return f[i];
}
int ans = inf;
for (int j = i; j < n; ++j) {
if (gcd(nums[i], nums[j]) > 1) {
ans = Math.min(ans, 1 + dfs(j + 1));
}
}
f[i] = ans;
return ans;
}
private int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | class Solution {
public:
const int inf = 0x3f3f3f3f;
int validSubarraySplit(vector<int>& nums) {
int n = nums.size();
vector<int> f(n);
function<int(int)> dfs = [&](int i) -> int {
if (i >= n) return 0;
if (f[i]) return f[i];
int ans = inf;
for (int j = i; j < n; ++j) {
if (__gcd(nums[i], nums[j]) > 1) {
ans = min(ans, 1 + dfs(j + 1));
}
}
f[i] = ans;
return ans;
};
int ans = dfs(0);
return ans < inf ? ans : -1;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 | func validSubarraySplit(nums []int) int {
n := len(nums)
f := make([]int, n)
var dfs func(int) int
const inf int = 0x3f3f3f3f
dfs = func(i int) int {
if i >= n {
return 0
}
if f[i] > 0 {
return f[i]
}
ans := inf
for j := i; j < n; j++ {
if gcd(nums[i], nums[j]) > 1 {
ans = min(ans, 1+dfs(j+1))
}
}
f[i] = ans
return ans
}
ans := dfs(0)
if ans < inf {
return ans
}
return -1
}
func gcd(a, b int) int {
if b == 0 {
return a
}
return gcd(b, a%b)
}
|