questions C

This commit is contained in:
2025-05-25 10:16:13 +01:00
parent 1e0aebfbfd
commit e6a22f98f8
85 changed files with 16925 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
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
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`)
&nbsp;
**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
&nbsp;
**Step 3: Return the result**
- If the final GCD equals `1`, return `True`
- Otherwise, return `False`
&nbsp;
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.