Number of Ways to Earn Points — Hard Problem & Solution
An exam has several types of question. types[i] = [counti, marksi] means there are counti questions of the i-th type, each worth marksi marks.
- Difficulty: Hard
- Topics: Arrays, Dynamic Programming
- Asked at: Amazon, Google, Flipkart
- Time limit: 2 s
- Memory limit: 256 MB
- Languages: JavaScript, TypeScript, Python, Java, C++, C, C#, Go, Kotlin, Swift, Rust, PHP and Ruby
Problem statement
An exam has several types of question. types[i] = [count_i, marks_i] means there are count_i questions of the i-th type, each worth marks_i marks. Questions of the same type are indistinguishable.
Return the number of ways to earn exactly target marks, modulo 10^9 + 7.
Example 1
Input: target = 6, types = [[6,1],[3,2],[2,3]]
Output: 7
Example 2
Input: target = 5, types = [[50,1],[50,2],[50,5]]
Output: 4
Example 3
Input: target = 18, types = [[6,1],[3,2],[2,3]]
Output: 1
Explanation: Only answering every question reaches 18.
Constraints
1 <= target <= 1000n == types.length1 <= n <= 50types[i].length == 21 <= count_i, marks_i <= 50
How to solve Number of Ways to Earn Points
A bounded knapsack over question types. Because same-type questions are indistinguishable, a scheme is fully described by how many of each type it answers — so each type contributes one loop over its allowed counts.
Approach
- Start with
dp[0] = 1. - For each type
(count, marks), build a fresh array: for every reachable totaltand everycin0 … count, adddp[t]intondp[t + c·marks]while it stays withintarget. - Return
dp[target]at the end.
Why it works
Using a fresh array per type is what enforces the bound: without it, an in-place upward sweep would let a type be used unboundedly, and a downward sweep would cap it at one. Processing one type at a time in a fixed order counts each multiset of answers exactly once.
Complexity
- Time —
O(n · target · maxCount) - Space —
O(target)
Pitfalls
- An in-place sweep gives the unbounded or 0/1 knapsack, not the bounded one.
- The target must be hit exactly;
dp[target]is the answer, not a running sum. - Reduce modulo on every addition.
Reference solution
Python
from typing import List
def waysToReachTarget(target: int, types: List[List[int]]) -> int:
MOD = 1000000007
dp = [0] * (target + 1)
dp[0] = 1
for count, marks in types:
ndp = [0] * (target + 1)
for t in range(target + 1):
if dp[t] == 0:
continue
c = 0
while c <= count and t + c * marks <= target:
ndp[t + c * marks] = (ndp[t + c * marks] + dp[t]) % MOD
c += 1
dp = ndp
return dp[target]JavaScript
var waysToReachTarget = function(target, types) {
var MOD = 1000000007;
var dp = [];
for (var t0 = 0; t0 <= target; t0++) dp.push(0);
dp[0] = 1;
for (var i = 0; i < types.length; i++) {
var count = types[i][0], marks = types[i][1];
var ndp = [];
for (var b = 0; b <= target; b++) ndp.push(0);
for (var t = 0; t <= target; t++) {
if (dp[t] === 0) continue;
for (var c = 0; c <= count && t + c * marks <= target; c++) {
ndp[t + c * marks] = (ndp[t + c * marks] + dp[t]) % MOD;
}
}
dp = ndp;
}
return dp[target];
};Also on the editorial tab: C, C#, C++, Go, Java, Kotlin, PHP, Ruby, Rust, Swift, TypeScript.