Ngay khi đọc đề bài này, thì nhận ra dạng bài này liền. Trước tiên, bắt tay vào giải đệ quy trước:

  • Đầu tiên, tại mỗi question ta có 2 lựa chọn: solve question i này hoặc skip tới câu tiếp theo question i+1
  • Nếu chọn solve question i hiện tại, thì ta gain đc point của question hiện tại và skip brainpower i question tiếp theo
  • Nếu skip question i, ta tiến thới xét question i+1 và tiếp tục xét 2 lựa chọn cho question i+1

Recursion + memo

class Solution {
    private int[][] questions;
    private Map<Integer, Long> memo;

    public long mostPoints(int[][] questions) {
        this.questions = questions;
        memo = new HashMap<>();

        return solve(0);
    }

    private long solve(int i) {
        if (i >= questions.length) return 0;
        if (memo.containsKey(i)) return memo.get(i);

        long maxPoint = Math.max(
            // select this question (gain questions[i][0] points) i, skip brainpower i question
            questions[i][0] + solve(i + questions[i][1] + 1),
            solve(i+1) // ignore question i => check next question i + 1
        );

        memo.put(i, maxPoint);
        return maxPoint;
    }
}

Sau khi có đc công thức đệ quy, dễ dàng convert thành quy hoạch động (à, thật ra cũng không dễ lắm :)))

class Solution {
    public long mostPoints(int[][] questions) {
        int n = questions.length;
        long[] dp = new long[200001];

        for (int i = n-1; i >= 0; i--) {
            dp[i] = Math.max(
                questions[i][0] + dp[i + questions[i][1] + 1],
                dp[i+1]
            );
        }

        return dp[0];
    }
}