LeetCode 3629.通过质数传送到达终点的最少跳跃次数:埃式筛+BFS
【LetMeFly】3629.通过质数传送到达终点的最少跳跃次数:埃式筛+BFS
力扣题目链接:https://leetcode.cn/problems/minimum-jumps-to-reach-end-via-prime-teleportation/
给你一个长度为n的整数数组nums。
你从下标 0 开始,目标是到达下标n - 1。
在任何下标i处,你可以执行以下操作之一:
- 移动到相邻格子:跳到下标
i + 1或i - 1,如果该下标在边界内。 - 质数传送:如果
nums[i]是一个质数p,你可以立即跳到任何满足nums[j] % p == 0的下标j处,且下标j != i。
返回到达下标n - 1所需的最少跳跃次数。
质数是一个大于 1 的自然数,只有两个因子,1 和它本身。
示例 1:
输入:nums = [1,2,4,6]
输出:2
解释:
一个最优的跳跃序列是:
- 从下标
i = 0开始。向相邻下标 1 跳一步。 - 在下标
i = 1,nums[1] = 2是一个质数。因此,我们传送到索引i = 3,因为nums[3] = 6可以被 2 整除。
因此,答案是 2。
示例 2:
输入:nums = [2,3,4,7,9]
输出:2
解释:
一个最优的跳跃序列是:
- 从下标
i = 0开始。向相邻下标i = 1跳一步。 - 在下标
i = 1,nums[1] = 3是一个质数。因此,我们传送到下标i = 4,因为nums[4] = 9可以被 3 整除。
因此,答案是 2。
示例 3:
输入:nums = [4,6,5,8]
输出:3
解释:
- 由于无法进行传送,我们通过
0 → 1 → 2 → 3移动。因此,答案是 3。
提示:
1 <= n == nums.length <= 1051 <= nums[i] <= 106
解题方法:埃式筛+BFS
首先我们求出从2 22到10 6 10^6106每个数的质因数有哪些,即p r i m e s [ i ] primes[i]primes[i]是i ii的所有质因数列表:(注意由于5 55能跳到下一个5 55,所以我们把一个质数自身也视为它的质因数)
第一层循环用变量i ii从2 22到10 6 10^6106,若p r i m e s [ i ] primes[i]primes[i]为空,说明没有数是p r i m e s [ i ] primes[i]primes[i]的因数,说明i ii是质数。
对于质数i ii,有:2 i 2i2i、3 i 3i3i、⋯ \cdots⋯全部都不是质数,并且i ii是它们每个数的因数,将i ii加入p r i m e s [ t × i ] primes[t\times i]primes[t×i]中。
我们创建一个j u m p s jumpsjumps哈希表,令j u m p s [ p ] jumps[p]jumps[p]是值为p pp的元素可以通过质数传送跳到的所有下标:
遍历一遍n u m s numsnums数组,对于n u m s [ i ] nums[i]nums[i],它的所有质因数为p r i m e s [ n u m s [ i ] ] primes[nums[i]]primes[nums[i]]列表,也就是说对于在p r i m e s [ n u m s [ i ] ] primes[nums[i]]primes[nums[i]]中的每一个数p pp,都能通过质数传送跳到下标i ii。
对于p r i m e s [ n u m s [ i ] ] primes[nums[i]]primes[nums[i]]中的每一个n u m s [ i ] nums[i]nums[i]的质因数p pp,将i ii放入j u m p s [ p ] jumps[p]jumps[p]列表中。
好了,j u m p s jumpsjumps“地图”构建好了,剩下的就是普通的广度优先搜索B F S BFSBFS算法了:
首先处理完所有跳跃0 00次可以到达的点、然后处理所有跳跃1 11次可到达的点、然后处理所有跳跃2 22次可到达的点、…、直到处理过程中跳到了n u m s numsnums的最后一个元素为止。
具体而言,首先将所有跳跃0 00次可以到达的点的下标(即0 00)入队,使用一个布尔类型的数组记录每个下标是否被处理过(入队后即视为处理过,被处理过的元素不再次入队)。
每次队列中的所有元素都是“再一步”可以跳到的点,将此时队列中所有元素出队并将这些元素一步可以跳到的点入队到下一个队列。
一个元素一步都能跳到哪些位置呢?
- 移动到相邻格子:i ii可以跳到i − 1 i-1i−1和i + 1 i+1i+1(如果不越界的话)
- 质数传送:即跳跃到每个处在j u m p s [ n u m s [ i ] ] jumps[nums[i]]jumps[nums[i]]中的位置
额外的,由于n u m s numsnums中元素可能相同,例如[ 5 , 5 , 10 , 7 , 15 , 20 , 25 ] [5, 5, 10, 7, 15, 20, 25][5,5,10,7,15,20,25],为了避免第一个5 55跳到所有5 55的倍数[ 10 , 15 , 20 , 25 ] [10, 15, 20, 25][10,15,20,25]这些位置后第二个5 55再次尝试跳到这些位置(然后发现这些位置都被标记过了),可以在第一个5 55jump完它所有的倍数后将j u m p s [ 5 ] jumps[5]jumps[5]数组清空。
- 时间复杂度:预处理O ( M log l o g M ) O(M\log log M)O(MloglogM),单次算法n log M n\log MnlogM,其中M = 10 6 M=10^6M=106
- 空间复杂度:O ( n log M ) O(n\log M)O(nlogM)
AC代码
C++
/* * @LastEditTime: 2026-05-08 20:23:18 */constintM=1000001;vector<int>primes[M];intinit=[]{for(inti=2;i<M;i++){if(primes[i].empty()){// i是质数for(intj=i;j<M;j+=i){primes[j].push_back(i);// i是j的因数}}}return0;}();classSolution{private:inlinevoidtoQueue(queue<int>&q,vector<bool>&visited,intn){if(visited[n]){return;}q.push(n);visited[n]=true;}public:intminJumps(vector<int>&nums){unordered_map<int,vector<int>>jumps;for(inti=0;i<nums.size();i++){for(intp:primes[nums[i]]){jumps[p].push_back(i);}}vector<bool>visited(nums.size());queue<int>q;toQueue(q,visited,0);for(intans=0;;ans++){queue<int>nextQueue;while(q.size()){intnow=q.front();q.pop();if(now==nums.size()-1){returnans;}toQueue(nextQueue,visited,now+1);if(now){toQueue(nextQueue,visited,now-1);}for(intnext:jumps[nums[now]]){toQueue(nextQueue,visited,next);}jumps[nums[now]].clear();// 防止重复遍历}swap(q,nextQueue);}}};Python
''' LastEditTime: 2026-05-08 21:10:33 '''fromtypingimportListfromcollectionsimportdefaultdictfromitertoolsimportcount M=1000001primes=[[]for_inrange(M)]foriinrange(2,M):ifnotprimes[i]:forjinrange(i,M,i):primes[j].append(i)classSolution:defpush(self,q:List[int],n:int):ifself.visited[n]:returnq.append(n)self.visited[n]=TruedefminJumps(self,nums:List[int])->int:jumps=defaultdict(list)foriinrange(len(nums)):forpinprimes[nums[i]]:jumps[p].append(i)q=[]self.visited=[False]*len(nums)self.push(q,0)foransincount(0):next_queue=[]fornowinq:ifnow==len(nums)-1:returnans self.push(next_queue,now+1)ifnow:self.push(next_queue,now-1)fornextinjumps[nums[now]]:self.push(next_queue,next)jumps[nums[now]].clear()q=next_queue同步发文于CSDN和我的个人博客,原创不易,转载经作者同意后请附上原文链接哦~
千篇源码题解已开源
