2601. Prime Subtraction Operation

题目 #

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

思路1 筛质数+二分 #

分析 #

  • 1000以内的质数全部先找出来
  • 每个数都尽可能小就好了,那么就是找质数里面小于当前到前一个差值的最大的那个

代码 #

 1func oulerPrimes(mx int, primes *[]int) {
 2	flag := make([]bool, mx+1) // 标记数有没有被筛掉,false就是没有
 3	for i := 2; i < mx+1; i++ {
 4		if !flag[i] {
 5			// 数没有被比自己小的数筛掉,就代表是质数
 6			*primes = append(*primes, i)
 7		}
 8		for _, v := range *primes {
 9			if i*v > mx {
10				break
11			}
12			// 每一个数都作为因子乘以比自己小的素数筛掉后面的数
13			flag[i*v] = true
14			if i%v == 0 {
15				// 减少时间复杂度的关键算法
16				// 12 = 2 * 3 * 2,i = 4时,只排了8就退出了,因为6会将12排除
17				// 也就是,假设v可以整除i即i = kv,有某个数为x = mi = kmv
18				//        那么存在一个数 i < km < x可以把x排掉,用i乘以所有的质数去排除就没什么意义了,提前退出减少时间复杂度
19				break
20			}
21		}
22	}
23}
24
25var primes []int = make([]int, 0, 168)
26
27func init() {
28	oulerPrimes(1000, &primes)
29}
30
31func primeSubOperation1(nums []int) bool {
32	pre := 0
33	for _, v := range nums {
34		// 每个数都尽可能小
35		if v <= pre {
36			return false
37		}
38		// 找到能尽可能接近pre的最大质数,反过来就是找v-pre之间的最大质数
39		i := sort.SearchInts(primes, v-pre) - 1
40		if i < 0 {
41			pre = v
42		} else {
43			pre = v - primes[i]
44		}
45	}
46	return true
47}

思路2 倒着做 #

分析 #

  • 和上面一样,只是上面不好理解,倒着好理解,没上面写的快
  • 尽可能让每一个数减去质数后小于但最接近后一个

代码 #

 1func oulerPrimes(mx int, primes *[]int) {
 2	flag := make([]bool, mx+1) // 标记数有没有被筛掉,false就是没有
 3	for i := 2; i < mx+1; i++ {
 4		if !flag[i] {
 5			// 数没有被比自己小的数筛掉,就代表是质数
 6			*primes = append(*primes, i)
 7		}
 8		for _, v := range *primes {
 9			if i*v > mx {
10				break
11			}
12			// 每一个数都作为因子乘以比自己小的素数筛掉后面的数
13			flag[i*v] = true
14			if i%v == 0 {
15				// 减少时间复杂度的关键算法
16				// 12 = 2 * 3 * 2,i = 4时,只排了8就退出了,因为6会将12排除
17				// 也就是,假设v可以整除i即i = kv,有某个数为x = mi = kmv
18				//        那么存在一个数 i < km < x可以把x排掉,用i乘以所有的质数去排除就没什么意义了,提前退出减少时间复杂度
19				break
20			}
21		}
22	}
23}
24
25var primes []int = make([]int, 0, 168)
26
27func init() {
28	oulerPrimes(1000, &primes)
29}
30
31func primeSubOperation(nums []int) bool {
32	n := len(nums)
33	last := nums[n-1]
34	for i := n - 2; i >= 0; i-- {
35		v := nums[i]
36		if v < last {
37			last = v
38			continue
39		}
40		for _, vp := range primes {
41			if vp >= v {
42				return false
43			}
44			if v-vp < last {
45				v -= vp
46				break
47			}
48		}
49		if v >= last {
50			return false
51		}
52		last = v
53		continue
54	}
55	return true
56}