LeetCode2020春季编程赛 个人题解 (暂完成D题)

D题 切割数组

题目链接 : https://leetcode-cn.com/problems/qie-fen-shu-zu/

题目大意:
拆分原来的一个数组,拆分后的每个数组满足最左和最优元素 g c d > 1 gcd>1 gcd>1 求最小拆分次数。

算法涉及:简单数论,dp
题解思路:
先手写了几个数据,否决了贪心的做法,强行把每个数组扩成最大 这样的做法不是最优的。
考虑dp的思路,大概的复杂度是 O ( n m ) O(n\sqrt{m}) O(nm ) n n n表示数组长度 m m m表示数组中的元素大小
就是一个循环,嵌套一个根号唯一分解 a i a_i ai的复杂度。

首先 用 d p [ i ] dp[i] dp[i]表示 1 − i 1-i 1i号元素全部分成合法的数组的最小花费。
d p [ i ] dp[i] dp[i] 可以转移的方向必然是前面另一个元素 a j a_j aj a i a_i ai有共同的因子,但 d p [ i ] dp[i] dp[i]不是从 d p [ j ] dp[j] dp[j]转移过来的,是以 j j j为新数组的左端点, i i i为新数组右端点,新建一个数组,且前面的元素保证最小花费构成合法的划分,这个转移方法来转移的。
如果不新设一个数组来表示如上的含义,你就得二重循环来找了,这样复杂度肯定不对。
新设一个数组 v a l [ x ] val[x] val[x]表示在下标 i i i之前,最小的花费满足以含有 x x x因子的数为右边界,左边元素构成合法划分。

这样, d p dp dp转移方程就是: d p [ i ] = m i n ( d p [ i ] , v a l [ x ] + 1 ) dp[i]=min(dp[i],val[x]+1) dp[i]=min(dp[i],val[x]+1) 其中 x x x表示 a i a_i ai的一个素因子。

v a l [ x ] val[x] val[x]在转移的过程中也需要更新,注意到 a i a_i ai也含有因子 x x x,那么dp[i-1]表示的是 [ 1 , i − 1 ] [1,i-1] [1,i1]号元素的最小花费,即 v a l [ x ] val[x] val[x]的转移方向。
v a l val val的转移方程: v a l [ x ] = m i n ( v a l [ x ] , d p [ i − 1 ] ) val[x]=min(val[x],dp[i-1]) val[x]=min(val[x],dp[i1])

这题卡常有点顶,直接根号唯一分解因数T了,就先线筛一下,然后直接查找素数表就过了。

#define ll long long
const int maxn=1e6+5;
class Solution {

public:
    bool vis[maxn];
    int prime[maxn/10];
    int tot=0;
    void init()
    {
        memset(vis,0,sizeof(vis));
        vis[1]=1;
        for(int i=2;i<maxn;i++)
        {
            if(!vis[i])
            {
                prime[tot++]=i;
            }
            for(int j=0;j<tot&&i*prime[j]<maxn;j++)
            {
                vis[i*prime[j]]=1;
                if(i%prime[j]==0)
                {
                    break;
                }
            }
        }   
    }

    vector<int>getf(int a)
    {
        vector<int>vv;
        for(int i=0;prime[i]*prime[i]<=a;i++)
        {
            int x=prime[i];
            if(a%x==0)
            {
                vv.push_back(x);
                while(a%x==0)a/=x;
            }
        }
        if(a!=1)vv.push_back(a);
        return vv;
    }

    int splitArray(vector<int>& nums) 
    {
        int n = nums.size();
        init();
        int dp[100005];//到a[i]为止全部分好的最小代价
        int val[1000005]; //以x为右端点 左边元素划分的最小代价
        dp[0]=0;
        memset(val,1,sizeof(val));

        vector<int>v;

        for(int i=1;i<=n;i++)
        {
            dp[i]=dp[i-1]+1;
            v=getf(nums[i-1]);
            for(auto x:v)
            {
                //cout<
                dp[i]=min(dp[i],val[x]+1);
            }
            for(auto x:v)
            {
                val[x]=min(val[x],dp[i-1]);
            }

        }
        
        return dp[n];
       
        //return 0;
    }
};

剩余坑待填

你可能感兴趣的:(dp,数学,数论)