Leetcode 2140. Solving intellectual problems

Posted by joozt on Mon, 24 Jan 2022 06:52:06 +0100

Title Link

Game 276 week 3
https://leetcode-cn.com/problems/solving-questions-with-brainpower/

Topic content

Give you a two-dimensional integer array questions with subscript starting from 0, where questions[i] = [pointsi, brainpoweri].

This array represents a series of questions in an exam. You need to solve them in order (that is, starting from question 0), and choose to solve or skip each question. Solving problem i will give you a pointsi score, but you will not be able to solve the next brainpower i problems (that is, you can only skip the next brainpower i problems). If you skip question i, you can decide which action to use for the next question.

For example, here are your questions = [[3,2], [4,3], [4,4], [2,5]]:
If problem 0 is solved, you can get 3 points, but you can't solve problems 1 and 2.
If you skip question 0 and solve question 1, you will get 4 points, but you can't solve questions 2 and 3.
Please return to the highest score you can get in this exam.

Solution ideas

Bare DFS (timeout)

Pure dfs writing is to set the boundary. If the number of questions exceeds the number of questions, it will be returned. Otherwise, in question I, you should not skip this question and enter the next question; Or do this question and skip questions[i][1].

class Solution {
public:
    int ans;
    Solution()
    {
        ans=0;
    }
    void dfs(vector<vector<int>>& questions,int step,int score)
    {
        if(step>=questions.size())//If the current question number exceeds the number of questions, it means that a question making method has been enumerated to query the answer of this scheme
        {
            if(score>ans)
            {
                ans=score;
                return;
            }
        }
        else
        {
            dfs(questions,step+questions[step][1]+1,score+questions[step][0]);//Do the current topic
            dfs(questions,step+1,score);//Skip this topic
        }
    }
    long long mostPoints(vector<vector<int>>& questions) {
        dfs(questions,0,0);
        return ans;
    }
};

This method will timeout in the 21st test case because this recursive method contains many repeated calculations, so we need to optimize it.

Memory search

The above method contains a large number of repetitions, so we adopt the memory search method to save the value obtained each time, so that it can be returned directly in the subsequent dfs.

typedef long long ll;
class Solution {
public:
    int n;
    vector<ll>dp;
    void dp_init(int n)
    {
        dp.resize(n+1);
    }
    ll dfs(vector<vector<int>>& questions,int i)
    {
        if(i>=n)
            return 0;
        else if(dp[i])
            return dp[i];//If it's worth saving, return directly without repeated recursion
        else
            return dp[i]=max(dfs(questions,i+1),dfs(questions,i+questions[i][1]+1)+questions[i][0]);//Take the maximum of the two cases
    }
    long long mostPoints(vector<vector<int>>& questions) {
        n = questions.size();
        dp_init(n);
        return dfs(questions,0);
    }
};


All test cases can be passed by this method

dynamic programming

However, the efficiency of recursive method is always slower than that of recursive method because it involves stack operation. Therefore, can we solve it by recursive (dynamic programming)?
Using dynamic programming, we can set dp[i] as the maximum score for solving the i-th to n-1 st problems, and in the selection of each state, we have two states to choose or not. Therefore, when selected, dp[i]=questions[i][0]+dp[i+questions[i][1]+1]; If not selected, dp[i]=dp[i+1]. We just need to compare the two answers and take the maximum.
In addition, it should be noted that since the optimal substructure size at this time is in the order from back to front, the smaller I, the larger the structure size contained in dp[i]! Therefore, we also need to traverse the order from back to front and reverse the order to dp fill in the form!

typedef long long ll;
class Solution {
public:
    long long mostPoints(vector<vector<int>>& questions) {//Reverse dp
        int n = questions.size();
        vector<ll>dp(n+1);
        int i;
        for(i=n-1;i>=0;i--)
        {
            if(i+ll(questions[i][1])+1>=n)//If it is out of bounds, you can directly return the current question score.
            {
                dp[i] = max(ll(questions[i][0]),dp[i+1]);
            }
            else
            {
                dp[i] = max(questions[i][0]+dp[i+questions[i][1]+1],dp[i+1]);//Otherwise, select or deselect the two cases
            }
        }
        return dp[0];//The initial unpacking included the whole scale
    }
};


Thus, compared with the memory method, the space-time scale has been reduced to a certain extent.

Attention

The most important point of this problem is the reverse dynamic programming. In the past, the traditional dynamic programming problems traverse i from 0 to n, and this problem is just the opposite, which shows that our traversal order is closely related to the inclusion coverage relationship between the optimal substructure to a great extent. When we choose the traversal direction of i, we must select it according to the coverage relationship of the substructure. The scale of the post traversal structure is generally larger than that of the front!

Topics: Algorithm leetcode Dynamic Programming