leetcode
leetcode 2301 ~ 2350
质数减法运算

质数减法运算

难度:

标签:

题目描述

You are given a 0-indexed integer array nums of length n.

You can perform the following operation as many times as you want:

  • Pick an index i that you haven’t picked before, and pick a prime p strictly less than nums[i], then subtract p from nums[i].

Return true if you can make nums a strictly increasing array using the above operation and false otherwise.

A strictly increasing array is an array whose each element is strictly greater than its preceding element.

 

Example 1:

Input: nums = [4,9,6,10]
Output: true
Explanation: In the first operation: Pick i = 0 and p = 3, and then subtract 3 from nums[0], so that nums becomes [1,9,6,10].
In the second operation: i = 1, p = 7, subtract 7 from nums[1], so nums becomes equal to [1,2,6,10].
After the second operation, nums is sorted in strictly increasing order, so the answer is true.

Example 2:

Input: nums = [6,8,11,12]
Output: true
Explanation: Initially nums is sorted in strictly increasing order, so we don't need to make any operations.

Example 3:

Input: nums = [5,8,3]
Output: false
Explanation: It can be proven that there is no way to perform operations to make nums sorted in strictly increasing order, so the answer is false.

 

Constraints:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i] <= 1000
  • nums.length == n

代码结果

运行时间: 33 ms, 内存: 16.2 MB


// 题目思路:
// 1. 与普通Java代码相似,但这里我们会尽量使用Java Stream来操作数据。
// 2. 先判断数组是否已经是严格递增的,如果是则直接返回true。
// 3. 如果不是,则需要对每个元素尝试减去小于它的质数,使得数组变为严格递增。
// 4. 为了实现这个,我们需要一个辅助函数来生成小于等于某个数的所有质数。

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class SolutionStream {
    private List<Integer> generatePrimes(int n) {
        return IntStream.rangeClosed(2, n)
                        .filter(this::isPrime)
                        .boxed()
                        .collect(Collectors.toList());
    }

    private boolean isPrime(int num) {
        if (num < 2) return false;
        return IntStream.rangeClosed(2, (int) Math.sqrt(num))
                        .noneMatch(i -> num % i == 0);
    }

    public boolean canBeIncreasing(int[] nums) {
        int n = nums.length;
        List<Integer> primes = generatePrimes(1000);
        for (int i = 0; i < n; i++) {
            for (int prime : primes) {
                if (prime < nums[i]) {
                    nums[i] -= prime;
                    boolean isIncreasing = IntStream.range(1, n)
                                                    .allMatch(j -> nums[j] > nums[j - 1]);
                    if (isIncreasing) return true;
                    nums[i] += prime;
                } else {
                    break;
                }
            }
        }
        return false;
    }
}

解释

方法:

此题解采用了逆序处理数组元素的策略,从后向前调整数组,确保每个元素都比前一个元素小。首先,通过筛法(埃拉托斯特尼筛法)预先计算出所有1000以内的质数,并存储在数组中。对于数组中的每个元素,如果当前元素比后一个元素大或相等,则计算两者之差,并在质数列表中找到恰好大于此差的最小质数,使用该质数来减少当前元素的值。如果在任何点上,找到的质数大于或等于当前元素,或者后一个元素小于2(因为没有小于2的质数可以使用),则返回false。如果整个数组处理完毕没有遇到问题,则返回true。

时间复杂度:

O(n log p)

空间复杂度:

O(p)

代码细节讲解

🦆
在逆序处理数组时,为什么从倒数第二个元素开始而不是从数组的最后一个元素开始?
在逆序处理数组的时候,我们需要比较当前元素与其后一个元素的大小,并可能需要对当前元素进行调整。从倒数第二个元素开始是因为数组的最后一个元素没有后一个元素可以比较,所以从倒数第二个元素开始能确保每个参与比较的元素都有一个后续的元素。
🦆
如果在处理过程中,当前元素减去恰好大于差的质数后变为负数怎么办?
在题解中,算法已经通过条件检查确保在减去质数之前,质数必须小于当前元素。这是通过检查质数是否大于等于当前元素来实现的。如果没有这个检查,当前元素减去一个大于自身的质数确实可能导致结果为负数,这会违背题目的逻辑要求。因此,这种情况在逻辑上被规避了。
🦆
为什么在找到恰好大于差的质数时还需要检查质数是否大于等于当前元素?这里的逻辑是什么?
检查质数是否大于等于当前元素是为了确保当前元素减去这个质数后不会变为负数或零。因为如果质数大于或等于当前元素,那么减去这个质数后的结果将不符合题目要求,即每个元素都应该是正整数。这样的检查是为了防止程序在运行过程中产生逻辑错误或不合理的结果。
🦆
在题解中提到,如果后一个元素小于2则无法进行操作,这是基于什么原理?是否说明所有元素必须大于1才能保证程序正常运行?
基于质数的定义,质数是大于1的自然数且仅能被1和它本身整除的数。如果数组中的任何元素小于2,那么在减去任何质数后都不可能得到一个有效的正整数结果。因此,如果后一个元素小于2,就无法找到一个合适的质数来进行减法操作,从而无法继续执行题解中的逻辑。是的,所有元素必须大于1才能保证程序正常运行。

相关问题