Problem¶
You are given a 0-indexed 2D integer array questions
where questions[i] = [pointsi, brainpoweri]
.
The array describes the questions of an exam, where you have to process the questions in order (i.e., starting from question 0
) and make a decision whether to solve or skip each question. Solving question i
will earn you pointsi
points but you will be unable to solve each of the next brainpoweri
questions. If you skip question i
, you get to make the decision on the next question.
- For example, given
questions = [[3, 2], [4, 3], [4, 4], [2, 5|3, 2], [4, 3], [4, 4], [2, 5]]
:- If question
0
is solved, you will earn3
points but you will be unable to solve questions1
and2
. - If instead, question
0
is skipped and question1
is solved, you will earn4
points but you will be unable to solve questions2
and3
.
- If question
Return the maximum points you can earn for the exam.
Constraints:
1 <= questions.length <= 105
questions[i].length == 2
1 <= pointsi, brainpoweri <= 105
Example 1:
**Input:** questions = [[3,2],[4,3],[4,4],[2,5|3,2],[4,3],[4,4],[2,5]]
**Output:** 5
**Explanation:** The maximum points can be earned by solving questions 0 and 3.
- Solve question 0: Earn 3 points, will be unable to solve the next 2 questions
- Unable to solve questions 1 and 2
- Solve question 3: Earn 2 points
Total points earned: 3 + 2 = 5. There is no other way to earn 5 or more points.
Solve¶
Info: https://leetcode.com/problems/solving-questions-with-brainpower/
This is the one I come up with:
class Solution:
def mostPoints(self, questions: List[List[int]]) -> int:
self.n = questions.__len__()
self.trace = [[0,0] for i in range(self.n)]
i = 0
for v,bp in questions[::-1]:
if i > 0:
self.trace[i][0]=max(self.trace[i-1][0], self.trace[i-1][1])
if i-bp-1<0:
self.trace[i][1] = v
else:
self.trace[i][1] = v + max(self.trace[i-bp-1][0], self.trace[i-bp-1][1])
i+= 1
return max(self.trace[self.n-1][0], self.trace[self.n-1][1])
First look¶
Problem analised¶
Giving the problem with the skipping, it get a bit tricky:
- Let say you go from the first to last question,
- If there is only one question
questions[0]
, then chose to do the first questionquestions[0]
will give the most point. We havemaxPoint( questions[0] ) = q[0].value
- But if there is 2 question, there will be 2 case:
- We want to do the second question, which lead to 2 child case:
- We want to do only the second question:
- This mean
questions[1].value >= question[0].value
questions[0]
.brainPower >= 1: which mean if we try to do it, we need to skip at least 1 question, meanning skip the second questionquestions[1]
- maxPoint(
questions[0..1]
) = q[1].value
- This mean
- We can do both questtion
questions[0]
.brainPower == 0: which mean if we try to do it, we can still do the second questionquestions[1]
- maxPoint(
questions[0..1]
) = q[1].value + q[0].value
- We want to do only the second question:
- We don’t want to do the second question
- This mean
questions[1]
. value <question[0]
.value questions[0]
.brainPower >= 1: which mean if we try to do it, we need to skip at least 1 question, meanning skip the second questionquestions[1]
- maxPoint(
questions[0..1]
) = q[0].value
- This mean
- We want to do the second question, which lead to 2 child case:
- Let keep it going: add the third question, the best point of current could be:
- We want to do the third question
- We want to do only the third question
- This mean
questions[2]
. value >= the maxPoint(question[0..1]) - Also,
questions[i]
.brainPower >= 1 + i (i=[0..1]) : which mean if we try to do it, we need to skip the third questionquestions[2]
- We could see a recusive approad here
- This mean
- We could do the third question, but we need to find
questions[i]
.brainPower < 1 + i (i=[0..1]): which mean if we do it, we can still do the third questionquestions[2]
- Here, we can see a bad partent:
- If we can find a matching
questions[i]
.brainPower < 1+ i: maxPoint(question[0..i]) still isn’t mean we have usedquestions[i]
in it - Which lead to a seperated:
- maxPoint(question[0..i]) that use
questions[i]
and have the correspondingquestions[i]
.brainPower - maxPoint(question[0..i]) that not
questions[i]
and we need to trace back which is the lastquestion[j]
(j != i) being done, and what is it’s corresponding brainPower
- maxPoint(question[0..i]) that use
- This could mean we have to handle traceback/max table for each of there case
- If we can find a matching
- We want to do only the third question
- We skip the third question: Well, which mean we can keep the maxPoint(question[0..1]) of two last question
- We want to do the third question
- If there is only one question
- This could mean we have a O(n^2) if we not careful to handle the trace back and find the best maxPoint(question[0..i])
First implementation¶
Still, here could be the answer
class Solution:
def mostPoints(self, questions: List[List[int]]) -> int:
self.n = questions.__len__()
# Define trace array for trace back -1 mean we have no traceback; first value is the last question we do when we not do the question [i], second value is when we do the question [i]
self.trace = [[-1,i] for i in range(self.n)]
# Define max array to give us a maxPoint table for cache the result instead of calling mostPoint over and over; Simmilar to trace, the first value is when we not do the question [i], second one is when we do the question[i]
self.max = [[0,0] for i in range(self.n)]
i = 0
for v,bp in questions:
# Handle the first one
if i==0:
self.max[i][0]= 0
self.max[i][1] = questions[0][0]
self.trace[i][0] = -1
self.trace[i][1] = 0
continue
# find max case
innerMax = 0
innerTrace = -1
for innerIndex in range(i):
if self.trace[innerIndex][0] != -1:
if innerMax < self.max[innerIndex][0]:
innerMax = self.max[innerIndex][0]
innerTrace = self.trace[innerIndex][0
if innerMax < self.max[innerIndex][0]:
innerMax = self.max[innerIndex][0]
innerTrace = self.trace[innerIndex][0]
# TODO............
i+= 1
return max(self.trace[self.n-1][0], self.trace[self.n-1][1])
The code it self become too much of a head ache; and also, the double for loop make me feel like there should be a better solution.
This is when i revaluate the aproad for better solution and also, fix the mess code above.
Revaluate¶
- We could go back-ward, doing the last question back to the first question.
- This make the brainPower from from skip “ahead” infomation problem (Which later information being rely on a lot of trace back info and need all history data for processing) :
MaxPoint(question[0..i]) = max(question[i].value + maxPoint(question[0..i-1]) that isn't *require* question[i] to be skip, maxPoint(question[0..i-1]))
- into a skip “behind” problem (Which we can predicted exact infomation needed): when doing question[i], the maxPoint value after skipping question[i].brainPower will alway be
question[i].value + maxPoint([i+question[i].brainPower .. n])
without any condition:maxPoint(question[i..n]) = max(question[i].value + maxPoint([i+question[i].brainPower .. n]), maxPoint(question[i+1..n]))
- This make the brainPower from from skip “ahead” infomation problem (Which later information being rely on a lot of trace back info and need all history data for processing) :
- Now that more like it; we not need to go and code to finding something weird like
that isn't *require* question[i] to be skip
Recusive approad¶
class Solution:
def mostPoints(self, questions: List[List[int]]) -> int:
if questions.__len__() == 0:
return 0
n = questions.__len__()
v = questions[0][0]
bp = questions[0][1]
return max(v + self.mostPoints(questions[bp+1:n]), self.mostPoints( questions[1:n]))
quite slow tho; also, this is when i learn about @cache
in python; it can’t be use for List type, so just global the question infomation and instead using index value as parameter. Here is a updated code
class Solution:
@cache
def maxPoints(self, x):
if x>=self.n:
return 0
v = self.questions[x][0]
bp = self.questions[x][1]
return max(v + self.maxPoints(x+bp+1), self.maxPoints(x+1))
def mostPoints(self, questions: List[List[int]]) -> int:
self.n = questions.__len__()
self.questions = questions
return maxPoints(0)
Dynamic approad¶
Yeah, just do it at this point; These should be better to understand than my first submit
class Solution:
def mostPoints(self, questions: List[List[int]]) -> int:
n = questions.__len__()
maxTable = [0]*n
maxTable[n-1] = questions[n-1][0]
i = n-2
for v, bp in questions[::-1]:
if i+bp+1 < n:
v += maxTable[i+bp+1]
maxTable[i] = max(v, maxTable[i+1])
i -= 1
return maxTable[0]
- The first submit code [i] value will be separate if I do it or not
- I using hard code to separate the len = 1 (which turn into an if statement inside the for loop in the first submit code)
- Invert the for loop index value from n down to 1; instead couting up 1 to n like in the first submit code.
Created : August 16, 2023