Files
codetutor/backend/data/questions/calculate-amount-paid-in-taxes.yaml

195 lines
9.7 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
title: Calculate Amount Paid in Taxes
slug: calculate-amount-paid-in-taxes
difficulty: easy
leetcode_id: 2303
leetcode_url: https://leetcode.com/problems/calculate-amount-paid-in-taxes/
categories:
- arrays
- math
patterns:
- slug: greedy
is_optimal: true
function_signature: "def calculate_tax(brackets: list[list[int]], income: int) -> float:"
test_cases:
visible:
- input: { brackets: [[3, 50], [7, 10], [12, 25]], income: 10 }
expected: 2.65
- input: { brackets: [[1, 0], [4, 25], [5, 50]], income: 2 }
expected: 0.25
- input: { brackets: [[2, 50]], income: 0 }
expected: 0.0
hidden:
- input: { brackets: [[10, 10]], income: 10 }
expected: 1.0
- input: { brackets: [[5, 100]], income: 5 }
expected: 5.0
- input: { brackets: [[10, 0], [20, 50]], income: 15 }
expected: 2.5
- input: { brackets: [[100, 10], [200, 20], [300, 30]], income: 250 }
expected: 45.0
description: |
You are given a **0-indexed** 2D integer array `brackets` where `brackets[i] = [upper_i, percent_i]` means that the i<sup>th</sup> tax bracket has an upper bound of `upper_i` and is taxed at a rate of `percent_i`. The brackets are **sorted** by upper bound (i.e., `upper_{i-1} < upper_i` for `0 < i < brackets.length`).
Tax is calculated as follows:
- The first `upper_0` dollars earned are taxed at a rate of `percent_0`.
- The next `upper_1 - upper_0` dollars earned are taxed at a rate of `percent_1`.
- The next `upper_2 - upper_1` dollars earned are taxed at a rate of `percent_2`.
- And so on.
You are given an integer `income` representing the amount of money you earned. Return *the amount of money that you have to pay in taxes*. Answers within `10^-5` of the actual answer will be accepted.
constraints: |
- `1 <= brackets.length <= 100`
- `1 <= upper_i <= 1000`
- `0 <= percent_i <= 100`
- `0 <= income <= 1000`
- `upper_i` is sorted in ascending order
- All the values of `upper_i` are **unique**
- The upper bound of the last tax bracket is greater than or equal to `income`
examples:
- input: "brackets = [[3,50],[7,10],[12,25]], income = 10"
output: "2.65000"
explanation: "Based on your income, you have 3 dollars in the 1st tax bracket, 4 dollars in the 2nd tax bracket, and 3 dollars in the 3rd tax bracket. The tax rate for the three tax brackets is 50%, 10%, and 25%, respectively. In total, you pay $3 × 50% + $4 × 10% + $3 × 25% = $2.65 in taxes."
- input: "brackets = [[1,0],[4,25],[5,50]], income = 2"
output: "0.25000"
explanation: "Based on your income, you have 1 dollar in the 1st tax bracket and 1 dollar in the 2nd tax bracket. The tax rate for the two tax brackets is 0% and 25%, respectively. In total, you pay $1 × 0% + $1 × 25% = $0.25 in taxes."
- input: "brackets = [[2,50]], income = 0"
output: "0.00000"
explanation: "You have no income to tax, so you have to pay a total of $0 in taxes."
explanation:
intuition: |
Think of tax brackets like filling buckets of different sizes, where each bucket charges a different "fee" based on how much water you pour into it.
Imagine you have a series of buckets stacked on top of each other. The first bucket holds the first few dollars of income, the second bucket holds the next portion, and so on. Each bucket has its own tax rate — the "cost" of filling that bucket. Your income flows down through these buckets from top to bottom, and you pay the associated rate for however much fills each one.
The key insight is that brackets are **cumulative thresholds**, not individual bucket sizes. Each bracket's `upper` value tells you the total income covered *up to that point*. So the amount taxable at each rate is the difference between the current bracket's upper bound and the previous one's — that's the size of each "bucket."
Since we process income from the lowest bracket upward and stop once we've accounted for all income, this naturally follows a **greedy simulation**: handle each bracket in order, take as much income as fits, apply the rate, and move on.
approach: |
We solve this using a **Linear Simulation** approach:
**Step 1: Initialise tracking variables**
- `total_tax`: Set to `0.0` to accumulate the tax owed
- `previous_upper`: Set to `0` to track where the last bracket ended (first bracket starts from $0)
&nbsp;
**Step 2: Iterate through each bracket**
- For each `[upper, percent]` pair in `brackets`:
- Calculate the **taxable amount** in this bracket: `min(income, upper) - previous_upper`
- If `taxable_amount > 0`, add the tax: `taxable_amount * percent / 100`
- Update `previous_upper = upper` for the next iteration
- If `income <= upper`, we've covered all income — we can stop early
&nbsp;
**Step 3: Return the total tax**
- Return `total_tax` after processing all relevant brackets
&nbsp;
This greedy approach works because brackets are sorted by upper bound, so we naturally process income from lowest to highest rates in the order they apply.
common_pitfalls:
- title: Confusing Upper Bounds with Bracket Sizes
description: |
A common mistake is treating `upper` as the size of the bracket rather than a cumulative threshold.
For example, with `brackets = [[3,50],[7,10]]`, the second bracket doesn't cover 7 dollars — it covers dollars 4 through 7 (i.e., 4 dollars). The taxable amount in bracket `i` is `upper_i - upper_{i-1}`, not `upper_i` itself.
Always subtract the previous bracket's upper bound to get the actual taxable amount in each bracket.
wrong_approach: "Using upper directly as the taxable amount"
correct_approach: "Calculate upper - previous_upper for each bracket's taxable portion"
- title: Not Capping at Income
description: |
If your income is less than a bracket's upper bound, you don't fill the entire bracket — only the portion up to your income.
For example, with `income = 5` and a bracket `[10, 20]`, you don't tax 10 dollars at 20%. If the previous bracket ended at 3, you only tax `5 - 3 = 2` dollars at 20%.
Use `min(income, upper) - previous_upper` to ensure you never tax more than you earned.
wrong_approach: "Always using upper - previous_upper"
correct_approach: "Using min(income, upper) - previous_upper"
- title: Forgetting to Convert Percentage
description: |
The `percent` values are given as integers (e.g., `50` for 50%), not decimals. Forgetting to divide by 100 will give answers 100× too large.
Always calculate tax as `amount * percent / 100`.
key_takeaways:
- "**Bracket simulation pattern**: Process sorted intervals sequentially, tracking the boundary between processed and unprocessed portions"
- "**Cumulative vs incremental**: When given cumulative bounds, compute incremental amounts by subtracting the previous bound"
- "**Early termination**: Once income is fully covered, remaining brackets don't contribute — exit early for efficiency"
- "**Real-world relevance**: This exact logic is used in actual tax systems worldwide (progressive taxation)"
time_complexity: "O(n). We iterate through the brackets array once, where `n` is the number of tax brackets."
space_complexity: "O(1). We only use a constant number of variables (`total_tax`, `previous_upper`) regardless of input size."
solutions:
- approach_name: Linear Simulation
is_optimal: true
code: |
def calculate_tax(brackets: list[list[int]], income: int) -> float:
total_tax = 0.0
previous_upper = 0
for upper, percent in brackets:
# Calculate how much income falls in this bracket
# Cap at income if we don't fill the entire bracket
taxable_amount = min(income, upper) - previous_upper
# Only add tax if there's income in this bracket
if taxable_amount > 0:
total_tax += taxable_amount * percent / 100
# Move the boundary for the next bracket
previous_upper = upper
# Early exit: all income has been accounted for
if income <= upper:
break
return total_tax
explanation: |
**Time Complexity:** O(n) — We iterate through brackets at most once.
**Space Complexity:** O(1) — Only a few variables used.
We process each bracket in order, calculating how much of our income falls within that bracket's range. By tracking `previous_upper`, we know where each bracket starts. The `min(income, upper)` ensures we don't count income beyond what we actually earned. Early termination optimises for cases where income doesn't reach higher brackets.
- approach_name: Without Early Exit
is_optimal: false
code: |
def calculate_tax(brackets: list[list[int]], income: int) -> float:
total_tax = 0.0
previous_upper = 0
for upper, percent in brackets:
# Calculate taxable amount in this bracket
taxable_amount = min(income, upper) - previous_upper
# Add tax if positive (handles case where income < previous_upper)
if taxable_amount > 0:
total_tax += taxable_amount * percent / 100
previous_upper = upper
return total_tax
explanation: |
**Time Complexity:** O(n) — Always iterates through all brackets.
**Space Complexity:** O(1) — Only a few variables used.
This version always processes all brackets but still produces correct results because `taxable_amount` becomes 0 or negative once `income <= previous_upper`. The `if taxable_amount > 0` check prevents adding anything for brackets beyond our income. While functionally correct, the version with early exit is marginally more efficient.