LeetCode LCP 14. 切分数组--动态规划+质数筛
·
LCP 14. 切分数组
给定一个整数数组 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
题解
非常美妙的一个题目,我们一开始动态规划考虑,就是这样
int dp[1000000];
int splitArray(vector<int>& nums)
{
for(int i=1;i<=nums.size();i++)
{
dp[i]=1e9;
}
dp[0]=0;
dp[1]=1;
for(int i=2;i<=nums.size();i++)
{
for(int j=i;j>=1;j--)
{
if(__gcd(nums[i-1],nums[j-1])>1)
dp[i]=min(dp[i],dp[j-1]+1);
}
}
return dp[nums.size()];
}
上面这个代码应该很好理解,比较直观,就是每次找上一个最大公约数不为1的数字,进行动态规划判断,然后返回答案,但是这种是会超时的,因为每次都要往回找,n平方的时间复杂度。
我们需要进行一定优化,可以知道,我们要找到数字x和数字y的最大公约数大于1,说明这两个数字有着共同的质数因子,比如6和8质数因子为2,6和9质数因子为3,于是我们把题目转换为,我们先利用质数筛找出所有在1e6以内的质数,然后对每个数字进行质因数分解,如果数字nums[i]得到质数因子P1,P2,P3,那么动态规划方程就是(用dp_pre[x]表示质数因子x之前出现过的位置):
如果Px(x=1、2、3)之前没出现过:
dp[i]=min(dp[i],dp[i-1]+1);
dp_pre[Px]=i;//进行位置标记
否则:
dp[i]=min(dp[i],dp[dp_pre[Px]-1]+1);
dp[i]=min(dp[i],dp[i-1]+1);//要比较下是自己单独作为一个数组还是和前面共同因数的数字结合构成一个数组,哪种情况比较小
当然这样说不太对,因为注意看方程,是不断和dp[dp_pre[Px]-1]进行比较的,所以这个数值可能会有更小的,于是需要不断更新。
if(dp[i-1]<dp[dp_pre[prime[j]]-1])
dp_pre[prime[j]]=i;
AC代码
class Solution {
public:
int dp[100010];
int dp_pre[1000010];
vector<int>prime;
bool vis[1000010];
void init()//初始化素数筛
{
memset(vis,0,sizeof(vis));
for(int i=2;i<=1e6;i++)
{
for(int j=2;j*i<=1e6;j++)
{
vis[i*j]=true;
}
}
for(int i=2;i<=1e6;i++)
{
if(vis[i]==false)
{
prime.push_back(i);
}
}
}
int splitArray(vector<int>& nums)
{
init();
for(int i=0;i<prime.size();i++)
dp_pre[prime[i]]=-1;//记录每个质数因子能得到的最小的分组数的下标
for(int i=0;i<=nums.size();i++)//初始化
dp[i]=1e9;
dp[0]=0,dp[1]=1;
int x=nums[0];
while(x>1)
{
for(int i=0;i<prime.size();i++)
{
if(x%prime[i]==0)
{
x/=prime[i];
dp_pre[prime[i]]=1;
break;
}
}
}
for(int i=2;i<=nums.size();i++)
{
x=nums[i-1];
if(vis[x]==false)//特判为质数
{
if(dp_pre[x]==-1)
{
dp[i]=min(dp[i],dp[i-1]+1);
dp_pre[x]=i;
}
else
{
dp[i]=min(dp[i],dp[i-1]+1);
dp[i]=min(dp[i],dp[dp_pre[x]-1]+1);
}
if(dp[i-1]<dp[dp_pre[x]-1])
dp_pre[x]=i;
continue;
}
while(x>1)
{
for(int j=0;j<prime.size();j++)
{
if(x%prime[j]==0)
{
x/=prime[j];
if(dp_pre[prime[j]]==-1)
{
dp[i]=min(dp[i],dp[i-1]+1);//单独一个数字为一个组
dp_pre[prime[j]]=i;
}
else
{
dp[i]=min(dp[i],dp[dp_pre[prime[j]]-1]+1);
dp[i]=min(dp[i],dp[i-1]+1);//单独一个数字为一个组
if(dp[i-1]<dp[dp_pre[prime[j]]-1])//更新位置
dp_pre[prime[j]]=i;
}
break;
}
}
}
}
return dp[nums.size()];
}
};

太慢了,,,,,,不过有很多地方可以稍微优化下
更多推荐
所有评论(0)