Facebook Pixel

3610. Minimum Number of Primes to Sum to Target 🔒

Problem Description

You are given two integers n and m.

Your task is to select a multiset of prime numbers from the first m prime numbers such that the sum of the selected primes is exactly n. You may use each prime number multiple times.

Return the minimum number of prime numbers needed to sum up to n, or -1 if it is not possible.

For example, with n = 7 and m = 3 (the first 3 primes are 2, 3, 5), you can use the numbers 2 and 5 to reach 7 with two primes. If it's not possible to form n using these primes (even with repetitions), return -1.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

This problem is similar to the classic "coin change" problem. Each of the first m primes can be used any number of times, just like coins of different denominations. The aim is to make the total sum equal to n using as few of these primes as possible. Since order does not matter and repetitions are allowed, dynamic programming is a natural fit. By thinking about the solutions for smaller sums and how adding another prime can help reach the target, we can build up to the answer for n. This lets us efficiently find the minimum number of primes needed, or realize that it's impossible with the given set.

Solution Approach

First, generate the first m prime numbers by checking each number for primality and collecting them until we have enough.

Next, use dynamic programming to solve the problem, much like the "coin change" problem. Define an array f where f[i] represents the minimum number of primes needed to sum up to i. Initialize f[0] = 0 (since zero can always be made using zero primes), and set all other entries in f to infinity (or a very large value).

Iterate through each prime p among the first m primes. For every possible sum i from p to n, update f[i] as follows:

f[i] = min(f[i], f[i - p] + 1)

This means for each prime, at each step, we decide if adding one more instance of that prime helps achieve the sum with fewer elements.

After processing all the primes, if f[n] is still infinity, it's impossible to represent n as the sum of these primes, so return -1. Otherwise, return f[n], the minimum count found.

The approach is efficient because it reuses solutions to subproblems (smaller sums) and finds the answer in a bottom-up way.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's use n = 7 and m = 3 as an example. The first 3 primes are [2, 3, 5]. The goal is to find the minimum number of these primes (repetitions allowed) that sum to exactly 7.

Step 1: Generate the DP Array

  • Create an array f[0...7], initialized as f[0]=0 and the rest as infinity.

Step 2: Process Each Prime

Using prime = 2:

  • For i = 2, f[2] = min(f[2], f[0]+1) = 1
  • For i = 4, f[4] = min(f[4], f[2]+1) = 2
  • For i = 6, f[6] = min(f[6], f[4]+1) = 3

Array now: [0, ∞, 1, ∞, 2, ∞, 3, ∞]

Using prime = 3:

  • For i = 3, f[3] = min(f[3], f[0]+1) = 1
  • For i = 5, f[5] = min(f[5], f[2]+1) = 2
  • For i = 6, f[6] = min(3, f[3]+1) = 2
  • For i = 7, f[7] = min(∞, f[4]+1) = 3

Array now: [0, ∞, 1, 1, 2, 2, 2, 3]

Using prime = 5:

  • For i = 5, f[5] = min(2, f[0]+1) = 1
  • For i = 6, f[6] = min(2, f[1]+1) = 2 (no change, since f[1]=∞)
  • For i = 7, f[7] = min(3, f[2]+1) = 2

Final array: [0, ∞, 1, 1, 2, 1, 2, 2]

Step 3: Retrieve the Result

f[7] = 2, meaning the smallest number of primes from [2,3,5] needed to make 7 is 2.

How?

  • 2 + 5 = 7
  • 3 + 2 + 2 = 7 (requires 3 primes, which is more)

Conclusion: The minimum count is 2 (e.g., 2 and 5).

Solution Implementation

1import math
2
3# Generate the first M primes using trial division
4primes = []
5num = 2
6M = 1000
7while len(primes) < M:
8    is_prime = True
9    for p in primes:
10        if p * p > num:
11            break
12        if num % p == 0:
13            is_prime = False
14            break
15    if is_prime:
16        primes.append(num)
17    num += 1
18
19class Solution:
20    def minNumberOfPrimes(self, n: int, m: int) -> int:
21        """
22        Returns the minimum number of the first `m` primes that sum up to `n`.
23        If it's impossible, returns -1.
24        """
25        # Helper function for the minimum of two numbers
26        _min = lambda x, y: x if x < y else y
27
28        # Use a large constant to represent infinity
29        inf = float('inf')
30
31        # Initialize DP array: f[i] represents the minimum number of primes summing to i
32        f = [0] + [inf] * n
33
34        # Try each of the first `m` primes as a coin denomination
35        for prime in primes[:m]:
36            for i in range(prime, n + 1):
37                # State transition: either keep current value or use the new prime
38                f[i] = _min(f[i], f[i - prime] + 1)
39
40        # If inf remains, it's not possible to form n; otherwise, return the answer
41        return f[n] if f[n] < inf else -1
42
1import java.util.*;
2
3// This class provides methods to work with prime numbers and compute minimum counts for sum representations.
4class Solution {
5    // Static list storing the first M prime numbers.
6    static List<Integer> primes = new ArrayList<>();
7
8    // Static initialization block to populate the prime list.
9    static {
10        int candidate = 2;       // Candidate number to check for primeness.
11        int maxPrimes = 1000;    // Number of primes to precompute.
12        while (primes.size() < maxPrimes) {
13            boolean isPrime = true;
14            for (int prime : primes) {
15                // If prime*prime > candidate, no need to check further.
16                if (prime * prime > candidate) {
17                    break;
18                }
19                // If divisible, not a prime.
20                if (candidate % prime == 0) {
21                    isPrime = false;
22                    break;
23                }
24            }
25            // If candidate is prime, add to list.
26            if (isPrime) {
27                primes.add(candidate);
28            }
29            candidate++;
30        }
31    }
32
33    /**
34     * Returns the minimum number of prime numbers (from the first m primes)
35     * that sum up to n. If it's not possible, returns -1.
36     *
37     * This is a classic unbounded knapsack problem.
38     *
39     * @param n the target sum
40     * @param m the number of allowed primes (from the start)
41     * @return minimum count of primes needed to sum to n, or -1 if impossible
42     */
43    public int minNumberOfPrimes(int n, int m) {
44        int[] dp = new int[n + 1]; // dp[i]: min number of primes to sum to i
45        final int INF = 1 << 30;   // A large number representing infinity
46        Arrays.fill(dp, INF);
47        dp[0] = 0; // Zero primes needed for sum 0
48
49        // For each of the first m primes
50        for (int prime : primes.subList(0, m)) {
51            // For each sum >= prime
52            for (int sum = prime; sum <= n; sum++) {
53                // Unbounded knapsack: you can use this prime multiple times.
54                dp[s
55um] = Math.min(dp[sum], dp[sum - prime] + 1);
56            }
57        }
58        // If a valid answer was found, return it. Otherwise, return -1.
59        return dp[n] < INF ? dp[n] : -1;
60    }
61}
62
1class Solution {
2public:
3    int minNumberOfPrimes(int n, int m) {
4        // Static vector to cache the smallest 1000 prime numbers
5        static std::vector<int> primes;
6
7        // Initialize the prime list if it's empty
8        if (primes.empty()) {
9            int candidate = 2;
10            int targetPrimeCount = 1000;
11            while ((int)primes.size() < targetPrimeCount) {
12                bool isPrime = true;
13                // Check divisibility by previously found primes (up to sqrt(candidate))
14                for (int prime : primes) {
15                    if (prime * prime > candidate) break;
16                    if (candidate % prime == 0) {
17                        isPrime = false;
18                        break;
19                    }
20                }
21                if (isPrime) primes.push_back(candidate);
22                ++candidate;
23            }
24        }
25
26        // DP approach: f[i] = minimum number of primes needed to sum up to i
27        std::vector<int> f(n + 1, INT_MAX);
28        f[0] = 0; // Base case
29
30        // Use the first 'm' primes for the solution
31        for (int prime : std::vector<int>(primes.begin(), primes.begin() + m)) {
32            for (int sum = prime; sum <= n; ++sum) {
33                if (f[sum - prime] != INT_MAX) {
34                    f[sum] = std::min(f[sum], f[sum - prime] + 1);
35                }
36            }
37        }
38
39        // If a solution exists, return the minimum number of primes, else return -1
40        return f[n] < INT_MAX ? f[n] : -1;
41    }
42};
43
1// Generate the first 1000 prime numbers and store them in the 'primes' array
2const primes: number[] = [];
3let x = 2;              // Current number to check for primality
4const M = 1000;         // Number of primes to generate
5
6while (primes.length < M) {
7    let isPrime = true;
8    // Check if `x` is divisible by any previously found primes <= sqrt(x)
9    for (const prime of primes) {
10        if (prime * prime > x) break;
11        if (x % prime === 0) {
12            isPrime = false;
13            break;
14        }
15    }
16    if (isPrime) {
17        primes.push(x);
18    }
19    x++;
20}
21
22/**
23 * Computes the minimum number of primes (selected from the first `m` primes)
24 * that sum up to `n`. Returns -1 if no such combination exists.
25 *
26 * @param n - Target sum
27 * @param m - Number of first primes to use
28 * @returns Minimum number of primes needed, or -1 if impossible
29 */
30function minNumberOfPrimes(n: number, m: number): number {
31    const INF = 1e9;       // Represent infinity (unreachable state)
32    const dp: number[] = Array(n + 1).fill(INF);  // dp[i]: min number of primes to sum to i
33    dp[0] = 0;             // Base case: 0 can be formed by 0 primes
34
35    // Only use the first `m` primes
36    for (const prime of primes.slice(0, m)) {
37        // Classic unbounded knapsack dynamic programming loop
38        for (let i = prime; i <= n; i++) {
39            if (dp[i - prime] < INF) {
40                dp[i] = Math.min(dp[i], dp[i - prime] + 1);
41            }
42        }
43    }
44
45    // If not possible return -1, otherwise return the minimum count found
46    return dp[n] < INF ? dp[n] : -1;
47}
48

Time and Space Complexity

  • Time Complexity: The overall time complexity is O(m * n). This comes from the nested loops where for each of the first m primes, the code loops through possible sums up to n.
  • Space Complexity: The space complexity is O(n + M), where n is the target sum and M (here 1000) is the number of precomputed primes. This accounts for the f array of size n + 1 and the primes list of size M.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

Which of the following uses divide and conquer strategy?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More