题目描述
给定一个整数数组,将切分成若干个非空子数组,使得每个子数组最左边的数和最右边的数的最大公约数大于1。求出最少可以切成多少个子数组。
1
<
=
n
u
m
s
.
l
e
n
g
t
h
<
=
1
0
5
1 <= nums.length <= 10^5
1<=nums.length<=105
2
<
=
n
u
m
s
[
i
]
<
=
1
0
6
2 <= nums[i] <= 10^6
2<=nums[i]<=106
思路
记
f
[
i
]
f[i]
f[i]为前
i
i
i个数最少切成的数组个数,那么就有dp:
f
[
i
]
=
m
i
n
(
f
[
j
]
)
+
1
(
1
≤
j
≤
i
,
g
c
d
(
n
u
m
s
[
i
]
,
n
u
m
s
[
j
]
)
>
1
)
f[i]=min(f[j])+1 (1\le j \le i , gcd(nums[i],nums[j])>1 )
f[i]=min(f[j])+1(1≤j≤i,gcd(nums[i],nums[j])>1)
遍历
f
[
j
]
f[j]
f[j]的复杂度是
O
(
n
2
)
O(n^2)
O(n2)的,化简遍历,我们需要定向找到最大公约数大于1的
f
[
j
]
f[j]
f[j]。
对每一个新加进来的数
n
u
m
s
[
i
]
nums[i]
nums[i],计算它的质因数,我们要找到之前同样有这个质因数的
f
[
j
]
f[j]
f[j]的最小值,新加进来的数只要和那个
n
u
m
s
[
j
]
nums[j]
nums[j]放进同一个子数组中即可,所有质因数的最小情况的最小值就是
f
[
j
]
f[j]
f[j]的最小值,或者单独作为一个子数组,那么切割数就是
f
[
j
−
1
]
+
1
f[j-1]+1
f[j−1]+1。
到这里我们发现只有质因数的最小切割数是动态的,所以
f
[
i
]
f[i]
f[i]的含义转变为质数
i
i
i的最小切割子数组数,就有dp:
f
[
i
]
=
m
i
n
(
f
[
i
]
,
p
r
e
m
i
n
+
1
)
f[i]=min(f[i],premin+1)
f[i]=min(f[i],premin+1)
i
i
i是新加进的数的质因数,
p
r
e
m
i
n
premin
premin就是之前计算出的最小切割数。
预处理所有数的最小质因数,复杂度
O
(
M
+
N
l
o
g
M
)
O(M+NlogM )
O(M+NlogM)
AC代码
class Solution {
public:
int min_prime[1000005],prime[1000005];
int f[1000005];
void init(int n){
for(int i=2;i<=n;i++){
if(min_prime[i]==0){
prime[++prime[0]]=i;
min_prime[i]=i;
}
for(int j=1;(j<=prime[0])&&(i*prime[j]<=n);j++){
min_prime[i*prime[j]]=prime[j];
if(i%prime[j]==0)break;
}
}
for(int i=1;i<=n;i++){
f[i]=1000005;
}
return ;
}
int premin=0;
int splitArray(vector<int>& nums) {
init(1000000);
int len=nums.size();
for(int i=0;i<len;i++){
int res=premin;
premin=1000005;
while(nums[i]>1){
f[min_prime[nums[i]]]=min(f[min_prime[nums[i]]],res+1);
// printf("%d\n",f[min_prime[nums[i]]]);
premin=min(premin,f[min_prime[nums[i]]]);
nums[i]/=min_prime[nums[i]];
}
}
return premin;
}
};