[分解质因数+DP] LeetCode 1551.切分数组

题目描述

给定一个整数数组,将切分成若干个非空子数组,使得每个子数组最左边的数和最右边的数的最大公约数大于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(1ji,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[j1]+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;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值