title: Check If It Is a Good Array slug: check-if-it-is-a-good-array difficulty: hard leetcode_id: 1250 leetcode_url: https://leetcode.com/problems/check-if-it-is-a-good-array/ categories: - arrays - math patterns: - greedy function_signature: "def is_good_array(nums: list[int]) -> bool:" test_cases: visible: - input: { nums: [12, 5, 7, 23] } expected: true - input: { nums: [29, 6, 10] } expected: true - input: { nums: [3, 6] } expected: false hidden: - input: { nums: [1] } expected: true - input: { nums: [2, 4, 6, 8] } expected: false - input: { nums: [6, 9, 15, 21] } expected: false - input: { nums: [5, 7] } expected: true - input: { nums: [100, 99] } expected: true - input: { nums: [2, 3] } expected: true description: | Given an array `nums` of positive integers. Your task is to select some subset of `nums`, multiply each element by an integer and add all these numbers. The array is said to be **good** if you can obtain a sum of `1` from the array by any possible subset and multiplicand. Return `True` if the array is **good** otherwise return `False`. constraints: | - `1 <= nums.length <= 10^5` - `1 <= nums[i] <= 10^9` examples: - input: "nums = [12,5,7,23]" output: "true" explanation: "Pick numbers 5 and 7. 5*3 + 7*(-2) = 1" - input: "nums = [29,6,10]" output: "true" explanation: "Pick numbers 29, 6 and 10. 29*1 + 6*(-3) + 10*(-1) = 1" - input: "nums = [3,6]" output: "false" explanation: "Both 3 and 6 are divisible by 3, so any linear combination will also be divisible by 3, never equal to 1." explanation: intuition: | This problem might look intimidating at first — selecting subsets, multiplying by arbitrary integers, and summing to exactly `1`. But there's a beautiful mathematical theorem that makes this trivial once you recognise it. **Bézout's Identity** states that for any two integers `a` and `b`, there exist integers `x` and `y` such that `ax + by = gcd(a, b)`. This extends to any number of integers: given `a₁, a₂, ..., aₙ`, we can find integers `x₁, x₂, ..., xₙ` such that `a₁x₁ + a₂x₂ + ... + aₙxₙ = gcd(a₁, a₂, ..., aₙ)`. The key insight is that `1` can be expressed as such a linear combination **if and only if** the GCD of all numbers is `1`. Why? Because: - Any linear combination of the numbers must be divisible by their GCD - So if the GCD is greater than `1`, we can never reach `1` - And if the GCD equals `1`, Bézout's identity guarantees we *can* reach `1` Think of it like this: the GCD represents the "smallest building block" that all numbers share. If that building block is `1`, we can construct any integer (including `1`). If it's larger, we can only construct multiples of that larger value. approach: | With Bézout's Identity, the solution becomes remarkably simple: **Step 1: Initialise the running GCD** - `result`: Set to the first element of the array, or we can start with `0` (since `gcd(0, x) = x`)   **Step 2: Iterate through the array** - For each number in `nums`, compute `gcd(result, nums[i])` - Update `result` with this new GCD - **Early termination**: If `result` becomes `1` at any point, we can immediately return `True` — the GCD can never increase, and `1` is the smallest positive integer   **Step 3: Return the result** - If the final GCD equals `1`, return `True` - Otherwise, return `False`   The early termination optimisation is valuable because once we find two coprime numbers (GCD = 1), no further numbers can change this. common_pitfalls: - title: Overthinking the Subset Selection description: | The problem statement mentions "select some subset" and "multiply each element by an integer." This naturally leads to thinking about combinatorial approaches — trying different subsets, solving systems of equations, or using dynamic programming. However, this is a red herring. The mathematical insight (Bézout's Identity) tells us that the *existence* of such integers is guaranteed when GCD = 1. We don't need to find the actual coefficients, just verify the GCD condition. wrong_approach: "Dynamic programming or subset enumeration" correct_approach: "Simply compute the GCD of all elements" - title: Missing the GCD Connection description: | If you don't recognise this as a number theory problem, you might try brute force approaches that will time out. With `n` up to `10^5` elements and values up to `10^9`, any exponential or polynomial approach in terms of values won't work. The GCD approach is O(n log(max_value)), which handles even the largest inputs efficiently. wrong_approach: "Brute force subset enumeration" correct_approach: "Recognise the Bézout's Identity pattern" - title: Not Using Early Termination description: | While not strictly necessary for correctness, failing to check if the GCD becomes `1` early means unnecessary computation. Once two numbers are coprime, the overall GCD is locked at `1`. For example, with `[1000000, 7, 3, 3, 3, ...]` (many 3s), we find GCD = 1 after just the first two elements. Without early termination, we'd wastefully compute GCDs with all remaining elements. wrong_approach: "Always compute GCD with all elements" correct_approach: "Return True immediately when GCD reaches 1" key_takeaways: - "**Bézout's Identity**: A linear combination of integers can equal `d` if and only if `d` is a multiple of their GCD — the key mathematical insight" - "**Recognise number theory patterns**: Problems involving linear combinations and integer solutions often reduce to GCD checks" - "**Early termination**: When computing running GCD, stop as soon as you reach `1` — it can never decrease" - "**Don't overcomplicate**: What looks like a hard combinatorial problem becomes trivial with the right mathematical perspective" time_complexity: "O(n log M). We iterate through `n` elements, and each GCD computation takes O(log M) where M is the maximum element value (using Euclidean algorithm)." space_complexity: "O(1). We only track a single running GCD value regardless of input size." solutions: - approach_name: GCD with Early Termination is_optimal: true code: | from math import gcd from functools import reduce def is_good_array(nums: list[int]) -> bool: # Bézout's Identity: can reach 1 iff GCD of all elements is 1 # Use reduce to compute GCD across the entire array return reduce(gcd, nums) == 1 explanation: | **Time Complexity:** O(n log M) — n elements, each GCD takes O(log M) time. **Space Complexity:** O(1) — Only stores the running GCD. Python's `reduce` applies `gcd` cumulatively: `gcd(gcd(gcd(nums[0], nums[1]), nums[2]), ...)`. By Bézout's Identity, the array is "good" exactly when this overall GCD equals 1. - approach_name: Iterative GCD with Explicit Early Exit is_optimal: true code: | from math import gcd def is_good_array(nums: list[int]) -> bool: # Start with first element as our running GCD result = nums[0] for num in nums[1:]: # Update running GCD result = gcd(result, num) # Early termination: GCD of 1 can never decrease if result == 1: return True # Check if final GCD is 1 return result == 1 explanation: | **Time Complexity:** O(n log M) — worst case iterates all elements, but often exits early. **Space Complexity:** O(1) — Single variable tracks the running GCD. This version explicitly shows the early termination optimisation. Once we find two coprime numbers (GCD = 1), no additional elements can change this fact. This is particularly effective when the array contains diverse numbers likely to be coprime. - approach_name: Mathematical Proof Verification is_optimal: false code: | from math import gcd def is_good_array(nums: list[int]) -> bool: # Compute GCD of all elements step by step overall_gcd = 0 # gcd(0, x) = x, so this works as identity for num in nums: overall_gcd = gcd(overall_gcd, num) # Optimisation: exit early if GCD reaches 1 if overall_gcd == 1: return True # By Bézout's Identity: # ax + by = gcd(a,b) for some integers x, y # Extended to n numbers: we can reach gcd(nums) via linear combination # Therefore, we can reach 1 iff gcd(all nums) == 1 return overall_gcd == 1 explanation: | **Time Complexity:** O(n log M) — Same as optimal, with more explicit comments. **Space Complexity:** O(1) — Only the running GCD variable. This version documents the mathematical reasoning inline. Starting with `0` works because `gcd(0, x) = x`, making it a valid identity element. The extended Euclidean algorithm guarantees that if GCD = 1, integer coefficients exist to form the linear combination.