↓Skip to main content
  1. LeetCode/

LeetCode 2585: Number of Ways to Earn Points

·2 mins· ·
LeetCode Hard Dynamic-Programming Knapsack Array
Wei Yi Chung
Author
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
Table of Contents

Meta Data
#

Difficulty: hard First Attempt: 2026-08-27

  • Submissions: Accepted ×2

Approach
#

Questions of one type are indistinguishable, so each type decides how many to solve (0 to count). That is a bounded knapsack, or a grouped knapsack where each group (type) picks one option.

dp[s] = the ways to earn exactly s points, dp[0] = 1. For each type, run capacity backwards, adding for each s the ways that take k of this type: dp[s] += dp[s - k × marks].

Going backwards keeps dp[s - k × marks] at the value from the previous type, so a type is never counted twice.

My Solution
#

class Solution:
    def waysToReachTarget(self, target: int, types: List[List[int]]) -> int:
        MOD = 10 ** 9 + 7
        dp = [1] + [0] * target                    # dp[s]: ways to earn exactly s
        for count, marks in types:
            for s in range(target, 0, -1):         # backwards
                for k in range(1, count + 1):      # take k of this type
                    if s - k * marks < 0:
                        break
                    dp[s] = (dp[s] + dp[s - k * marks]) % MOD
        return dp[target]

Once s - k × marks goes negative, break, since larger k only goes further.

Complexity
#

Time O(target · Σ count), space O(target).

Mistakes to Avoid
#

  • Running capacity forwards: a type could then be used without limit.
  • Treating same-type questions as different: they are indistinguishable, so only the count matters.

Interview Explanation
#

Questions of one type are indistinguishable, so each type only decides how many to take, from 0 to count. This is a bounded knapsack: with a 1D dp, each type runs capacity backwards and enumerates how many to take inside, adding the matching ways. Going backwards keeps a type from being reused. Time O(target · Σ count).

Related

LeetCode 410: Split Array Largest Sum
·2 mins
LeetCode Hard Dynamic-Programming Binary-Search Greedy Prefix-Sum Array
LeetCode 410 note: binary search the answer; for a cap mid, greedily fill each segment to count the fewest segments, and mid works if that count is <= k
LeetCode 2355: Maximum Number of Books You Can Take
·2 mins
LeetCode Hard Dynamic-Programming Monotonic-Stack Stack Array
LeetCode 2355 note: ending at i, each shelf to the left takes one fewer book until a bottleneck shelf j with too few books; a monotonic stack finds j, and dp[i] = dp[j] + an arithmetic series
LeetCode 3989: Maximum Consistent Columns in a Grid
·2 mins
LeetCode Hard Dynamic-Programming Matrix Array
LeetCode 3989 note: the kept columns form a subsequence constrained only between neighbors; an LIS-style DP with dp[j] = the longest valid sequence ending at column j
LeetCode 1402: Reducing Dishes
·2 mins
LeetCode Hard Dynamic-Programming Greedy Sorting Array
LeetCode 1402 note: sort and take dishes from most satisfying down; each new dish goes first and shifts the chosen ones one slot later, adding the current chosen sum, so keep going while that sum is positive
LeetCode 1526: Minimum Number of Increments on Subarrays to Form a Target Array
·1 min
LeetCode Hard Dynamic-Programming Greedy Monotonic-Stack Array
LeetCode 1526 note: scanning left to right, every rise above the previous height needs that many new operations; drops reuse earlier operations
LeetCode 3651: Minimum Cost Path with Teleportations
·2 mins
LeetCode Hard Dynamic-Programming Grid-Dp Matrix Array
LeetCode 3651 note: compute layer by layer over teleports; arriving at a cell by teleport costs the previous layer’s minimum over cells with value >= it, found with a running minimum after sorting, followed by the usual right / down grid DP