abhaysp's blog

By abhaysp, history, 15 months ago, In English

I started practicing some DP problems. For some easy problems (like when there's only 1 arg whose value is changing in each recursive call) I'm able to change the memoized solution to tabulated solution. But I'm finding it hard for to do this when there are >= 2 args whose value is changing in each recursive call (and which are not very straightforward). For ex:.

Here's one question from leetcode contest (I hope pasting leetcode link here is not a problem): https://leetcode.com/contest/weekly-contest-359/problems/maximize-the-profit-as-the-salesman/

I was able to write a recursive (and thus memoized solution) but I couldn't figure out how to do it in tabulated form

Here's the memoized solution:

class Solution {
    vector<vector<int>> space;
public:
    int maximizeTheProfit(int n, vector<vector<int>>& offers) {
        int on = (int)offers.size();
        
        sort(offers.begin(), offers.end(), [&](vector<int>& o1, vector<int>& o2) {
            return o1.at(1) < o2.at(1);
        });
        
        this->space.resize(on, vector<int>(on, -1));
        return max_profit(offers, on, 0, -1);
    }
    
    int max_profit(const vector<vector<int>>& offers, int n, int idx, int prev) {
        if (idx == n) return 0;
        
        if (this->space[idx][prev + 1] != -1) return this->space[idx][prev + 1];
        
        int res = 0;
        if (idx == 0 || prev == -1 || (offers[prev][1] < offers[idx][0])) {
            res = max(res, offers[idx][2] + max_profit(offers, n, idx + 1, idx));
        }
        res = max(res, max_profit(offers, n, idx + 1, prev));
        
        return this->space[idx][prev + 1] = res; 
    }
};

For tabulation, I understand that outer loop should look like this:

for (int i = on - 1; i >= 0; i--) {
}

But I'm not able to understand how to write loop for the prev (inner loop). So far I understand that in tabulation, the prev should look between the i (i.e., from outer loop) and on (i.e., offers.size()).

Is there some blog/video and problems which can help me in getting good and converting memoized solution to tabulated ones ? I'm willing to practice more of these kinds of problem.

(Please comment if anything is not clear in the post.)

Thanks

  • Vote: I like it
  • +16
  • Vote: I do not like it

»
15 months ago, # |
  Vote: I like it +5 Vote: I do not like it

I'd love to help but I can't see the problem image, so could you fix this please?

  • »
    »
    15 months ago, # ^ |
      Vote: I like it +5 Vote: I do not like it

    Thanks, I fixed the link

    • »
      »
      »
      15 months ago, # ^ |
        Vote: I like it +5 Vote: I do not like it

      I think your solution is overly complicated. I'm still not really sure what the purpose of "prev" is. Could you explain?

      (I would further say that it is more beneficial to default to using tabulation instead of memoization because tabulation is much faster to implement (at least for me). The only difficulty, once you have all the transitions/base cases worked out, is to find the right order to compute the values.)

      • »
        »
        »
        »
        15 months ago, # ^ |
          Vote: I like it +5 Vote: I do not like it

        The intuition is, for the given offers, we start with take or not take approach (which would have O(2^n) time complexity). The special condition would be to check that interval don't overlap. For this, I've first sorted the offer as per end of range.

        prev is needed because we need to check the overlapping of interval of current offer with the prev taken offer.

        If you think the solution is overly complicated, you may feel free to suggest another approach, although that's not what my question is. My question is, how can I get good at changing memoized DP solution (which are not so straightforward) into tabulated solution.

        (For me, making a recursive solution seems far easier than directly jumping to iterative solution, if I get the hint/intuition that the problem will use DP and thus for now, I go like this: recursive -> memoize -> tabulation. If you know how to get better at directly making a tabulated solution (some questions/blogs etc.) please refer them)

        • »
          »
          »
          »
          »
          15 months ago, # ^ |
          Rev. 2   Vote: I like it 0 Vote: I do not like it

          Sorry if my response seemed a bit harsh.

          As far as I understand, your answer is contained in dp[0][-1] and transitions are like dp[i][prev] = max(dp[i][prev], dp[i + 1][i] + gold[i]) (where valid) and dp[i][prev] = max(dp[i][prev], dp[i + 1][prev]).

          So now we can see that i loops from n-1 to 0 (as you correctly stated), and then as far as I can tell it doesn't matter what order you loop prev, since dp[i][prev] only depends on certain values of dp[i + 1]. (so you could loop forwards or backwards)

          I think a good strategy is to write down the transitions on paper and from there the order of tabulation should come naturally.

»
15 months ago, # |
  Vote: I like it +5 Vote: I do not like it

Auto comment: topic has been updated by abhaysp (previous revision, new revision, compare).