问题描述:切分数组
给定一个整数数组 nums ,小李想将 nums 切割成若干个非空子数组,使得每个子数组最左边的数和最右边的数的最大公约数大于 1 。为了减少他的工作量,请求出最少可以切成多少个子数组。
示例 1:
输入:nums = [2,3,3,2,3,3] 输出:2
解释:最优切割为 [2,3,3,2] 和 [3,3] 。第一个子数组头尾数字的最大公约数为 2 ,第二个子数组头尾数字的最大公约数为 3 。
示例 2:
输入:nums = [2,3,5,7] 输出:4
解释:只有一种可行的切割:[2], [3], [5], [7]
限制:
1 <= nums.length <= 10^5
2 <= nums[i] <= 10^6
动态规划
假设 nums[0] - nums[i-1](最少可以切成多少个子数组)已知。会有2种情况
已知的组 + nums[i]这一组,就是 f[i] = f[i-1]+1
nums[i] 和 nums[0] - nums[i-1]间的某一个是的公约数大于1。
设 这某一个数的下标是j 所以 f[i] = f[j-1] + 1
预置一份 1 - 10^6内数字n的因子数组 minPrime[n]
对数组元素进行因子分解,并记录得到这个因子的位置(posMap)
设 n下标的元素是6 因子分解后记录的位置,posMap[2]=n, posMap[3]=n。
当其他元素分解出为2的因子时,获取$posMap[2]记录中的位置n。
所以此处可切,得到dp[n-1]时的结果 + 1。
例:
[10,6,3,5,7]
posMap - 记录得到这个因子的位置
dp - 记录执行到位置的次数
10
posMap[2] = 0;
posMap[5] = 0;
dp[0] = 1;
------------------------------------------
10, 6
posMap[3] = 1;
dp[1] = 1; // 已出现相同质因数2位置在0,0-1可以分,零开始直接 dp[1] = 1
------------------------------------------
10, 6, 3
dp[2] = 2; // 已出现相同质因数3位置在1。1-2可以分,取dp[1]记录值+1
------------------------------------------
10, 6, 3, 5
dp[3] = 1; // 已出现相同质因数5位置在0。0-2可以分,零开始直接 dp[3] = 1
------------------------------------------
10, 6, 3, 5, 7
posMap[7] = 4;
dp[4] = 2; // posMap未出现过7,取上次dp记录值+1
补课:
因子:假如整数n除以m,结果是无余数的整数,那么m就是n的因子。一个整数n的因子数为包含它自身的所有因子个数。例,12的因子数为6(1,2,3,4,6,12)
质数:质数又被称为素数,是指一个大于1的自然数,除了1和它自身外,不能被其它自然数整除的数叫做质数,其个数是无穷的,具有许多独特的性质,现如今多被用于密码学上。
约数:又称因数。整数a除以整数b(b≠0) 商是整数而没有余数,a能被b整除 或 b能整除a。a称为b的倍数,b称为a的约数。
互质:互质是公约数只有1的两个整数,叫做互质整数。公约数只有1的两个自然数,叫做互质自然数,后者是前者的特殊情形。
公约数:亦称“公因数”。它是一个能被若干个整数同时均整除的整数。如果一个整数同时是几个整数的约数,称这个整数为它们的“公约数”;公约数中最大的称为最大公约数。1总是它们的公因数。
质因数:在数论里是指能整除给定正整数的 质数。根据算术基本定理,不考虑排列顺序的情况下,每个正整数都能够以唯一的方式表示成它的质因数的乘积。
例:
99的因数 - 1,3,9,11,33,99
99的质因数- 3,11
33的因数 - 1,3,11,33
33的质因数- 3,11
99和33的公约数 - 3,11
作者:FlagMain
class Solution {
public int splitArray(int[] nums) {
// 预置 minPrime
int[] minPrime = new int[1000000 + 1];
for (int i = 2; i < minPrime.length; i++) {
if (minPrime[i] < 2) {
for (int j = i; j < minPrime.length; j += i) {
minPrime[j] = i;
}
}
}
// 记录执行到位置的次数
int[] dp = new int[nums.length];
// 记录因子位置
Map<Integer,Integer> posMap=new HashMap<Integer,Integer>();
for (int i = 0; i < nums.length; i++) {
// 预设次数
dp[i] = i > 0 ? dp[i - 1] + 1 : 1;
// 分解 $nums[$i] 的因子;
int n = nums[i];
while (n > 1) {
int factor = minPrime[n];
int minIndex = i;
if (posMap.containsKey(factor)) {
minIndex = posMap.get(factor);
}else {
posMap.put(factor, minIndex);
}
if (minIndex == 0) {
dp[i] = 1;
} else {
// 和 已记录处理的位置+1 对比去一个低的
if ( ( dp[minIndex - 1] + 1 ) < dp[i] ){
dp[i] = dp[minIndex - 1] + 1;
}
}
// 更新posMap
if (dp[i] < dp[minIndex]) {
posMap.put(factor, i);
}
n = n / factor;
}
}
return dp[nums.length - 1];
}
}