Dynamic Programming

Sujinยท2022๋…„ 12์›” 6์ผ
0

LeetCode

๋ชฉ๋ก ๋ณด๊ธฐ
9/24
post-thumbnail

๐Ÿ™‹๐Ÿป Bottom-up

๊ฐ€์žฅ ์ž‘์€ ๋ฌธ์ œ๋“ค ๋ถ€ํ„ฐ ๋‹ต์„ ๊ตฌํ•ด๊ฐ€๋ฉฐ ์ „์ฒด ๋‹ต์„ ์ฐพ๋Š” ๋ฐฉ์‹

ํ”ผ๋ณด๋‚˜์น˜ ํ•จ์ˆ˜ ๊ตฌํ˜„์„ ์˜ˆ๋กœ ๋“ค์ž๋ฉด,,

int fibonacci(int n)
{
    dp[0] = 0, dp[1] = 1;
    // ์ž‘์€ ๋ฌธ์ œ๋ถ€ํ„ฐ,,
    for (int i = 2; i <= n; i++)
        dp[i] = dp[i - 1] + dp[i - 2];
}

recursion์ด ์•„๋‹ˆ๋ผ, ์‹œ๊ฐ„๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰์„ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค

๐Ÿ™‹๐Ÿป Top-down

ํฐ ๋ฌธ์ œ๋ฅผ ๋ฐฉ๋ฌธ ํ›„ ์ž‘์€ ๋ฌธ์ œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๋‹ต์„ ์ฐพ๋Š” ๋ฐฉ์‹

int fibonacci(int n)
{
    if (n == 0) return 0;
    if (n == 1) return 1;
 
    if (dp[n] != -1) return dp[n];
 	
    // ํฐ๋ฌธ์ œ ๋ฐฉ๋ฌธํ•ด์„œ ๊ฑฐ๊ธฐ์„œ recursion์œผ๋กœ ์ž‘์€ ๋ฌธ์ œ๋ฅผ ํ˜ธ์ถœ! 
    dp[n] = fibonacci(n - 1) + fibonacci(n - 2);
    return dp[n];
}

์ ํ™”์‹์„ ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๋‹ค


๐Ÿ’ก Climbing Stairs

You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?

Example 1:

Input: n = 2
Output: 2
Explanation: There are two ways to climb to the top.
1. 1 step + 1 step
2. 2 steps

Example 2:

Input: n = 3
Output: 3
Explanation: There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step

Solution

๊ทผ๋ฐ ์™œ ์ด์ „ ๋‘๊ฐœ๋ฅผ ๋”ํ•˜๋Š”๊ฑฐ์•ผ,, ์ดํ•ด๊ฐ€ ์•ˆ๋ผ ๋‚˜ ๋ฐ”๋ณธ๊ฐ€

์•„ ! !!

๊ทธ๋‹ˆ๊นŒ ๋‚ด๊ฐ€ ์ง€๊ธˆ stair 2์— ์žˆ๋‹ค๊ณ  ์น˜์ž
๊ทธ๋Ÿผ ๋‚ด๊ฐ€ ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ณณ์€ stair 3๊ณผ 4์ด๋‹ค (ํ•œ๋ฒˆ์— 1 step or 2 step๋งŒ ๊ฐˆ ์ˆ˜ ์žˆ์œผ๋‹ˆ)
๊ทธ๋Ÿฌ๋ฏ€๋กœ stair 3์ด๋‚˜ 4์—์„œ 5๋กœ ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ๋”ํ•ด์ฃผ๋ฉด
๋‚ด๊ฐ€ stair 2์—์„œ 5๋กœ ๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ์˜ ์ˆ˜๊ฐ€ ๊ณ„์‚ฐ ๋˜๋Š” ๊ฒƒ์ด๋‹ค!

(notice: ๋งˆ์ง€๋ง‰ ๋‘๊ฐœ๋Š” ํ•ญ์ƒ 1๋กœ ์ฑ„์›Œ์ง€๊ณ  ์‹œ์ž‘ํ•œ๋‹ค)
์ƒ๊ฐํ•ด๋ณด์ž

stair 4 (๋งˆ์ง€๋ง‰ ๋‘๋ฒˆ์งธ)์—์„œ stair 5๋กœ (๋งˆ์ง€๋ง‰) ๊ฐ€๋Š” ๋ฐฉ๋ฒ•์€ ํ•˜๋‚˜๋‹ˆ๊นŒ ๋‹น์—ฐํžˆ 1
stair 5๋Š” ๋‹ค๋ฅธ ๋” ๋ฐ‘์— ๋‹จ๊ณ„ ๊ณ„๋‹จ์—์„œ 1๊ณ„๋‹จ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒƒ์„ ๋ฏธ๋ฆฌ ๊ณ ๋ คํ•ด์„œ ์ ์–ด์ค€๊ฒƒ!

Bottom up

class Solution {
public:
    int climbStairs(int n) {
        int one_step = 1;
        int two_step = 1;

        for (int i = 0; i <= n - 2; i++) { // ๋งˆ์ง€๋ง‰ ๋‘ ๊ณ„๋‹จ์€ 1๋กœ ์ด๋ฏธ ๊ณ„์‚ฐ ๋˜์–ด์žˆ์œผ๋‹ˆ, 
                                           //  ๊ทธ ๋งˆ์ง€๋ง‰ ๋‘ ๊ณ„๋‹จ ์ „๊นŒ์ง€๋งŒ loop์„ ๋Œ๋ฉด ๋œ๋‹ค
            int temp = one_step;
            one_step = one_step + two_step;
            two_step = temp;
        }

        return one_step;
    }
};

Top down

... ๋ชจ๋ฅด๊ฒŸ์‚ผ


๐Ÿ’ก Min Cost Climing Stairs

You are given an integer array cost where cost[i] is the cost of ith step on a staircase. Once you pay the cost, you can either climb one or two steps.

You can either start from the step with index 0, or the step with index 1.

Return the minimum cost to reach the top of the floor.

Example 1:

Input: cost = [10,15,20]
Output: 15
Explanation: You will start at index 1.
- Pay 15 and climb two steps to reach the top.
The total cost is 15.

Example 2:

Input: cost = [1,100,1,1,1,100,1,1,100,1]
Output: 6
Explanation: You will start at index 0.
- Pay 1 and climb two steps to reach index 2.
- Pay 1 and climb two steps to reach index 4.
- Pay 1 and climb two steps to reach index 6.
- Pay 1 and climb one step to reach index 7.
- Pay 1 and climb two steps to reach index 9.
- Pay 1 and climb one step to reach the top.
The total cost is 6.

Solution

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        int two = cost[0]; // min cost when taking the 2 stairs before
        int one = cost[1]; // min cost when taking the 1 stair before

        int curr = INT_MAX; // cost of the current stair, indeed the result

        for (int i = 2; i <= cost.size(); i++) {
            int c = 0;
            if (i != cost.size()) c = cost[i];

            curr = min(two + c, one + c);

            // update two, one for the next stari
            two = one;
            one = curr;
        }

        return curr;
    }
};

๐Ÿ’ก Longest Palindromic Substring

Given a string s, return the longest palindromic substring in s.

Example 1:

Input: s = "babad"
Output: "bab"
Explanation: "aba" is also a valid answer.

Example 2:

Input: s = "cbbd"
Output: "bb"

Solution

์ผ๋‹จ ์•„๊นŒ ์ง€ํ•˜์ฒ ์—์„œ ๋ดค๋˜ ์˜์ƒ์ด๋ผ ๋ณต๊ธฐ๋ฅผ ํ•ด๋ณด๋ฉด์„œ ํ˜ผ์ž ํ’€์–ด๋ณด์ž

์•„์ด๋””์–ด๋Š”:
each letter๊ฐ€ palindrome์˜ ๊ฐ€์šด๋ฐ๋ผ๊ณ  ์ƒ๊ฐํ•œ ์ƒํƒœ๋กœ ๊ณ„์† ์–‘์ชฝ์œผ๋กœ ํผ์ ธ๋‚˜๊ฐ€๋Š” ๊ฒƒ์ด๋‹ค
ํ•˜์ง€๋งŒ, palindrome์ด ์ง์ˆ˜์ผ์ˆ˜๋„ ์žˆ์„ํ…Œ๋‹ˆ, ๊ทธ๋Ÿด ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด i, i+1 ๋ฅผ ๋ฌถ์–ด์„œ ๊ฐ€์šด๋ฐ๋ผ๊ณ  ์ƒ๊ฐํ•˜์—ฌ ํผ์ ธ๋‚˜๊ฐ€๋Š” ๋ฐฉ๋ฒ•๋„ consider ํ•ด์ฃผ์–ด์•ผํ•œ๋‹ค

class Solution {
public:
    string longestPalindrome(string s) {
        int result_start = 0;
        int result_length = 0;

        for (int i = 0; i < s.size(); i++) {
            palindrome(s, i, i, result_start, result_length);
            palindrome(s, i, i+1, result_start, result_length);
        }

        return s.substr(result_start, result_length);
        
    }

private:
    void palindrome(string s, int i, int j, int &result_start, int &result_length) {
        while (i >= 0 && j < s.size()) {
            if (s[i] != s[j]) break;

            if (result_length < j - i + 1) {
                result_start = i;
                result_length = j - i + 1;
            }
            
            i--;
            j++;
        }
    }
};

๐Ÿ’ก Palindromic Substrings

Given a string s, return the number of palindromic substrings in it.

A string is a palindrome when it reads the same backward as forward.

A substring is a contiguous sequence of characters within the string.

Example 1:

Input: s = "abc"
Output: 3
Explanation: Three palindromic strings: "a", "b", "c".

Example 2:

Input: s = "aaa"
Output: 6
Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa".

Solution

์œ„์˜ ๋ฌธ์ œ๋ž‘ ๊ฑฐ์˜ ๋น„์Šทํ•˜๋‹ค! ์ด์ œ ๋ชจ๋“  ๊ฒฝ์šฐ์˜ ์ˆ˜๋ฅผ ์„ธ์–ด์ฃผ๊ธฐ๋งŒ ํ•˜๋ฉด ๋œ๋‹ค

class Solution {
public:
    int countSubstrings(string s) {
        int result = 0;

        for (int i = 0; i < s.size(); i++) {
            palindrome(s, i, i, result);
            palindrome(s, i, i+1, result);
        }

        return result;
        
    }

private:
    void palindrome(string s, int i, int j, int &result) {
        while (i >= 0 && j < s.size()) {
            if (s[i] != s[j]) break;

            result++;
            
            i--;
            j++;
        }
    }
};

๐Ÿ’ก Longest Increasing Subsequence

Given an integer array nums, return the length of the longest strictly increasing subsequence

Example 1:

Input: nums = [10,9,2,5,3,7,101,18]
Output: 4
Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4.

Example 2:

Input: nums = [0,1,0,3,2,3]
Output: 4

Example 3:

Input: nums = [7,7,7,7,7,7,7]
Output: 1

Solution

Subsequence๋ฅผ ๊ตฌํ•˜๋Š” ๋ฌธ์ œ๋ผ์„œ, ์–ด๋–ค element๋ฅผ ์‚ฌ์šฉํ• ์ง€ ์ฒ˜์Œ๋ถ€ํ„ฐ ๊ณ„์† ์ฒดํฌํ•˜์—ฌ ์—…๋ฐ์ดํŠธ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค

์ด๋Ÿฐ์‹์œผ๋กœ...

for (int i = 0; i < n; i++) {
	for (int j = 0; j < i; j++) {
    //...
    }
}

(Note: Subarray๋ฅผ ๊ตฌํ•˜๋Š” ๋ฌธ์ œ์—์„œ๋Š” ์กฐ๊ฑด์— ์–ด๊ธ‹๋‚˜๋ฉด ํ˜„์žฌ element๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ–ˆ์—ˆ๋‹ค!)

class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n = nums.size();
        vector<int> dp(n, 1);
        
        int result = 1;

        for (int i = 1; i < n; i++) {
            for (int j = 0; j < i; j++) {
                if (nums[j] < nums[i]) {
                    // nums[j]๋ฅผ ํฌํ•จํ•˜๋Š” subsequence๊ฐ€ ๋” ๊ธธ๋‹ค๋ฉด update ํ•ด์ค€๋‹ค
                    //  nums[i] ์ด์ „์˜ dp๊ฐ’๋“ค์€ ๋ชจ๋‘ ์ตœ์‹ (= the final longest subsequence) ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค
                    dp[i] = max(dp[i], dp[j] + 1);
                    
                }
            }
            result = max(result, dp[i]);
        }
        
        return result;
    }
};

๐Ÿ’ก Maximum Subarray

Given an integer array nums, find the subarray which has the largest sum and return its sum.

Example 1:

Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.

Example 2:

Input: nums = [1]
Output: 1

Example 3:

Input: nums = [5,4,-1,7,8]
Output: 23

Solution

nums[0:i+1]์˜ maximum subarray๋Š” ์ด ๋‘˜ ์ค‘ ํ•˜๋‚˜์ด๋‹ค

  1. ํ˜„์žฌ ์š”์†Œ(i)์˜ ๊ฐ’ ์ด๊ฑฐ๋‚˜
  2. ์ด์ „์— ๊ตฌํ–ˆ๋˜ ๋ถ€๋ถ„์ง‘ํ•ฉ์˜ ์ตœ๋Œ€ ๊ฐ’ + ํ˜„์žฌ ์š”์†Œ
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int result = INT_MIN;
        int cur_sum = 0;

        for (int i = 0; i < nums.size(); i++) {
            // cur_sum์€ ์ง€๊ธˆ๊นŒ์ง€ max sum์„ ๊ฐ€์ง€๊ณ ์žˆ๋Š” subarray์˜ sum์„ ๋ณด๊ด€
            //  ๊ทธ๋ž˜์„œ ์ง€๊ธˆ ์—ฌ๊ธฐ์„œ ๋ญ˜ ํ•˜๋Š”๊ฑฐ๋ƒ๋ฉด
            //  subarray๋ฅผ ์ดˆ๊ธฐํ™” ํ•˜๊ณ  ์—ฌ๊ธฐ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ• ๋•Œ๊ฐ€ ํฌ๋ƒ (nums[i])
            //  ์•„๋‹ˆ๋ฉด subarray๋กœ ๊ณ„์† ์ด์–ด์„œ ํ˜„์žฌ ๊ฐ’์„ ํฌํ•จํ–ˆ์„๋•Œ๊ฐ€ ๋” ํฌ๋ƒ (nums[i] + cur_sum)
            cur_sum = max(nums[i], nums[i] + cur_sum);
            result = max(cur_sum, result);
        }

        return result;
    }
};

cur_sum ์„ ๊ณ„์† max๋กœ keep up ํ•˜๋Š”๋ฐ ์™œ final answer์ด ์•„๋‹ˆ๋ƒ,,,๋ฉด
cur_sum์€ subarray์˜ ์‹œ์ž‘์œ„์น˜๊ฐ€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๋กœ ๊ณ ๋ ค๋˜๋ฉด์„œ ๊ฐ€์žฅ ์ตœ๊ทผ(not max) ์‹œ์ž‘์œ„์น˜์—์„œ๋ถ€ํ„ฐ์˜ max๋ฅผ following ํ•˜๊ธฐ ๋•Œ๋ฌธ์— result์™€ ๊ณ„์† ๋น„๊ตํ•ด์ฃผ๋ฉด์„œ updateํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค


๐Ÿ’ก Maximum Product Subarray

product๊ฐ€ ๊ฐ€์žฅ ํฐ subarray๋ฅผ ์ฐพ๋Š” ๋ฌธ์ œ์ด๋‹ค
subarray๋Š” continuousํ•œ element๋กœ๋งŒ ์ด๋ฃฐ ์ˆ˜ ์žˆ๋‹ค

.
.

์ด์ „ ๋ฌธ์ œ์™€ ๋˜๊ฒŒ ์œ ์‚ฌํ•ด ๋ณด์ด์ง€๋งŒ ์ƒ๊ฐํ•ด์•ผํ• ๊ฒŒ ํ•˜๋‚˜ ๋” ์žˆ๋‹ค (์ค‘์š”_

์Œ์ˆ˜๊ฐ€ ํ•˜๋‚˜๋งŒ ์žˆ์œผ๋ฉด ์ข‹์ง€๋งŒ, ๋‘๊ฐœ (ํ˜น์€ ์ง์ˆ˜๊ฐœ)๊ฐ€ ์žˆ๋‹ค๋ฉด maximum product์— ์˜ํ–ฅ์„ ์ค€๋‹ค.
์šฐ๋ฆฌ๋Š” ์–ด๋–ป๊ฒŒ ์Œ์ˆ˜๋ฅผ ํฌ๊ธฐํ• ์ง€ ์•„๋‹ˆ๋ฉด ๊ฐ€์ ธ๊ฐˆ์ง€ ๊ฒฐ์ •ํ•ด์•ผํ• ๊นŒ?

โ†’ track currMax & currMin

๋งŒ์•ฝ ํ˜„์žฌ ์ˆซ์ž๊ฐ€ ์Œ์ˆ˜๋ผ๋ฉด, currMin์— ๊ณฑํ•ด์ฃผ๋ฉด maximum product๋ฅผ ๊ฐ€์ ธ๋‹ค์ค„์ˆ˜๋„ ์žˆ๋Š”๊ฒƒ!
ํ˜„์žฌ ์ˆซ์ž๊ฐ€ ์–‘์ˆ˜๋ผ๋ฉด, currMax์— ๊ณฑํ•ด์ฃผ๋ฉด max product๋ฅผ ์ฃผ๊ฒŸ์ง•.
๊ตณ์ด if๋กœ ์–‘์ˆ˜ ์Œ์ˆ˜๋ฅผ ์ฒดํฌํ•˜์ง€ ์•Š์•„๋„ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
max(currMax * nums[i], currMin * nums[i])

์ด์ œ ์ด๋ ‡๊ฒŒ ๊ณฑํ•ด์ค€ ๊ฐ’๊ณผ์™€ ํ˜„์žฌ ์ˆซ์ž๋ฅผ ๋น„๊ต ํ–ˆ์„ ๋•Œ ๋” ํฐ์ˆ˜๋ฅผ currMax์— ์ €์žฅํ•˜์—ฌ subarray๋ฅผ ์žฌ์‹œ์ž‘ํ• ๊ฑด์ง€, ์ด์–ด๊ฐˆ๊ฑด์ง€ ์ •ํ•˜๋Š”๊ฑฐ๋‹ค

๋™์‹œ์— currMin๋„ updateํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค
์œ„์˜ ๋กœ์ง๊ณผ ๋น„์Šทํ•˜๊ฒŒ,
min(currMin * nums[i], currMax * nums[i]) ๋กœ ํ‘œํ˜„ํ•œ๋‹ค
๋‹จ, currMax๋ฅผ ์•ž์ „์—์„œ updateํ•ด์ฃผ์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, temp์— ๋ณด๊ด€ํ–ˆ์–ด์•ผ ํ•œ๋‹ค
๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ์ด ๊ฐ’๊ณผ ํ˜„์žฌ๊ฐ’์„ ๋น„๊ตํ–ˆ์„ ๋•Œ ๋” ์ž‘์€ ์ˆ˜๋ฅผ currMin์— ์ €์žฅํ•œ๋‹ค

Solution

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int currMax = nums[0];
        int currMin = nums[0];
        int result = nums[0];

        for (int i = 1; i < nums.size(); i++) {
            int temp = currMax;
            currMax = max(max(currMax * nums[i], currMin * nums[i]), nums[i]);
            currMin = min(min(temp * nums[i], currMin * nums[i]), nums[i]);

            result = max(result, currMax);
        }

        return result;               
    }
};

๐Ÿ’ก Unique Paths

There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.

Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner.

The test cases are generated so that the answer will be less than or equal to 2 * 10^9.

Example 1:

Input: m = 3, n = 7
Output: 28

Example 2:

Input: m = 3, n = 2
Output: 3
Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner:
1. Right -> Down -> Down
2. Down -> Down -> Right
3. Down -> Right -> Down

Solution

๊ฐ๊ฐ์˜ cell์—์„œ ๊ทธ๊ณณ๊นŒ์ง€์˜ unique path๋ฅผ ๊ณ„์‚ฐ ํ•ด์„œ ๋ณด๊ด€ํ•˜๋ฉด ๋˜๊ฒŸ๋‹น

์ง€๊ธˆ cell์—์„œ unique path๋ฅผ ๊ณ„์‚ฐ ํ•˜๋Š” ๋ฐฉ๋ฒ•์€
๋กœ๋ด‡์ด down or right๋งŒ ์›€์ง์ผ ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ–ˆ์œผ๋‹ˆ๊ฐ„ ..
์œ„์—์„œ ์™”๊ฑฐ๋‚˜ ์™ผ์ชฝ์—์„œ ์™”์„๊ฑฐ๋‹ˆ๊น,,

  • cell[i-1][i] (์œ„) + cell[i][i-1] (์™ผ์ชฝ) ํ•˜๋ฉด ๋˜๊ฒŸ๋‹น

๊ทธ๋ฆฌ๊ตฌ ,, cell์€ ๋ชจ๋‘ 0์œผ๋กœ initialize ํ•ด์•ผํ•˜๊ณ 
(except for the base cases)

  • base case๋Š” start robot์˜ ์•„๋ž˜, ์˜ค๋ฅธ์ชฝ cell์ด๋‹ค
    1๋กœ initialize ํ•˜๋ฉด ๋˜๊ฒŸ๊ตฐ ,,
    .
    .
    ์ด ์•„๋‹ˆ๋ผ base case๋Š”
    ์˜ค๋ฅธ์ชฝ ์ญ‰~ ํ•œ์ค„, ์•„๋ž˜ ์ญ‰~ ํ•œ์ค„์„ ๋‹ค 1๋กœ ์ดˆ๊ธฐํ™” ํ•ด์ค˜์•ผ๊ฒ ๋‹ค.
    ์ด๋ ‡๊ฒŒ ํ•ด์ฃผ์ง€์•Š์œผ๋ฉด for loop์„ index 1์—์„œ ์‹œ์ž‘ํ•˜๋ฏ€๋กœ
    1st row, 1st column์€ ๋ฐฉ๋ฌธ์ด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์— 0์œผ๋กœ ๊ธฐ๋ก๋˜์–ด์žˆ์–ด์„œ ํ‹€๋ฆฐ ๋‹ต์„ ๋ฑ‰์–ด๋‚ธ๋‹ค
class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> grid(m, vector<int>(n)); // initialized with 0

        // basecase
        // the row and column that contains the start cell
        for (int i = 0; i < m; i++) {
            grid[i][0] = 1;
        }
        for (int j = 0; j < n; j++) {
            grid[0][j] = 1;
        }
        
        // now dp!
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                grid[i][j] = grid[i-1][j] + grid[i][j-1];
            }
        }

        return grid[m-1][n-1];
    }
};

๐Ÿ’ก Unique Path II

You are given an m x n integer array grid. There is a robot initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.

An obstacle and space are marked as 1 or 0 respectively in grid. A path that the robot takes cannot include any square that is an obstacle.

Return the number of possible unique paths that the robot can take to reach the bottom-right corner.

Example 1:

Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
Output: 2
Explanation: There is one obstacle in the middle of the 3x3 grid above.
There are two ways to reach the bottom-right corner:
1. Right -> Right -> Down -> Down
2. Down -> Down -> Right -> Right

Example 2:

Input: obstacleGrid = [[0,1],[0,0]]
Output: 1

Solution

์œ„์˜ solution์—์„œ obstacle์— ๊ด€ํ•œ condition๋“ค๋งŒ ์กฐ๊ธˆ ๋” ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.
์ฃผ์–ด์ง„ obstacleGrid๋ฅผ dp๋กœ ๋ฎ์–ด์”Œ์›Œ ๊ณ„์‚ฐํ•˜๋Š”๊ฒŒ ํŠน์ง•์ด๋‹ค

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if (obstacleGrid[0][0] == 1)
            return 0;
        
        int m = obstacleGrid.size();
        int n = obstacleGrid[0].size();

        // reuse the obstacleGrid as the dp calculation matrix

        obstacleGrid[0][0] = 1; //the start point initialized as 1

        // the first row
        for (int i = 1; i < m; i++) {
            // if the current cell has the obstacle (has 1 in the cell)
            // or the previous cell had 0 path to the cell 
            //    (means there was a obstacle during this straight path journey)
            if (obstacleGrid[i][0] == 1 || obstacleGrid[i-1][0] == 0)
                obstacleGrid[i][0] = 0;
            else 
                obstacleGrid[i][0] = 1;
        }

        // the first column
        for (int j = 1; j < n; j++) {
            // ์—ฌ๊ธฐ๋„ ์œ„์—๋ž‘ ๋˜‘๊ฐ™์ด
            // ํ˜„์žฌ cell์— ์žฅ์• ๋ฌผ์ด ์žˆ๊ฑฐ๋‚˜ ํ˜น์€
            // ์ด์ „ cell์ด 0์œผ๋กœ mark๋˜์–ด์žˆ๋‹ค๋ฉด ์žฅ์• ๋ฌผ์ด ์ „์— ์–ด๋”˜๊ฐ€์— ์žˆ๋˜๋‹จ ๋œป
            if (obstacleGrid[0][j] == 1 || obstacleGrid[0][j-1] == 0)
                obstacleGrid[0][j] = 0;
            else
                obstacleGrid[0][j] = 1;
        }

        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (obstacleGrid[i][j] == 1) { 
                    obstacleGrid[i][j] = 0;
                } else {
                    obstacleGrid[i][j] = obstacleGrid[i-1][j] + obstacleGrid[i][j-1];
                }
            }
        }

        return obstacleGrid[m-1][n-1];

    }
};

๐Ÿ’ก House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.

Example 1:

Input: nums = [1,2,3,1]
Output: 4
Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.

Example 2:

Input: nums = [2,7,9,3,1]
Output: 12
Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1).
Total amount you can rob = 2 + 9 + 1 = 12.

Solution

class Solution {
public:
    int rob(vector<int>& nums) {
        int prev = 0;
        int curr = 0;
        int next = 0;

        for (int i = 0; i < nums.size(); i++) {
            next = max(prev + nums[i], curr);
            prev = curr;
            curr = next;
        }

        return curr;
    }
};

๐Ÿ’ก House Robber II

same as the previous question, except for that the house is are placed in a circle

Solution

์ง‘์ด ๋™๊ทธ๋ž—๊ฒŒ ์ด์–ด์ ธ ์žˆ์œผ๋ฏ€๋กœ ๋งˆ์ง€๋ง‰์ง‘์„ robํ•œ๋‹ค๋ฉด ์ฒซ๋ฒˆ์งธ ์ง‘์„ robํ•ด์„œ๋Š” ์•ˆ๋œ๋‹ค

class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if (n == 1) return nums[0];
		
        // ๋งŒ์•ฝ ์ฒซ๋ฒˆ์žฌ ์ง‘์„ robํ•œ๋‹ค๋ฉด ๋งจ ๋งˆ์ง€๋ง‰ ์ง‘์€ ํ›„๋ณด์—์„œ ์ œ์™ธ
        int from_0 = robber(nums, 0, n - 1);
        // ์ฒซ๋ฒˆ์งธ ์ง‘์„ robํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด ๋งˆ์ง€๋ง‰์ง‘๋„ robํ•  ๊ฐ€๋Šฅ์„ฑ ์—ด์–ด๋‘ 
        int from_1 = robber(nums, 1, n);

        return max(from_0, from_1);
    }
private:
    int robber(vector<int>& nums, int start, int end) {
        int prev = 0; // 2before    
        int curr = 0; // 1before
        int next = 0; // current

        for (int i = start; i < end; i++) {
            next = max(prev + nums[i], curr);
            prev = curr;
            curr = next;
        }

        return curr;
    }
};

๐Ÿ’ก Best Time to Buy and Sell Stock

You are given an integer array prices where prices[i] is the price of a given stock on the ith day.

On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any time. However, you can buy it then immediately sell it on the same day.

Find and return the maximum profit you can achieve.

Example 1:

Input: prices = [7,1,5,3,6,4]
Output: 7
Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
Total profit is 4 + 3 = 7.

Example 2:

Input: prices = [1,2,3,4,5]
Output: 4
Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.
Total profit is 4.

Example 3:

Input: prices = [7,6,4,3,1]
Output: 0
Explanation: There is no way to make a positive profit, so we never buy the stock to achieve the maximum profit of 0.

Solution

์ƒ๊ฐํ•ด๋ณด๋ฉด ๊ณ„์† ๊ฐ’์ด ์˜ค๋ฅธ๋‹ค๊ณ  ๋ณผ ๋•Œ

  • 3๋ฒˆ์งธ์— ์‚ฌ์„œ 6๋ฒˆ์งธ์— ํŒ๋งคํ•˜๋Š” ๊ฐ’ (6๋ฒˆ์งธ - 3๋ฒˆ์งธ)
  • 3๋ฒˆ์งธ์— ์‚ฌ์„œ 4๋ฒˆ์งธ์—์„œ ํŒ”๊ณ , ๋‹ค์‹œ 4๋ฒˆ์งธ์—์„œ ์‚ฌ์„œ 5๋ฒˆ์งธ์—์„œ ํŒ”๊ณ , ๋‹ค์‹œ 5๋ฒˆ์งธ์—์„œ ์‚ฌ์„œ 6๋ฒˆ์งธ์—์„œ ํŒ ๊ฐ’ (6๋ฒˆ์งธ - 5๋ฒˆ์จฐ + 5๋ฒˆ์งธ - 4๋ฒˆ์งธ + 4๋ฒˆ์จฐ - 3๋ฒˆ์งธ)

์ด ๋‘๊ฐ’์ด ๊ฒฐ๊ตญ ๊ฐ™์€ profit์ด๋‹ค
ํŒ๊ณณ์—์„œ ๋ฐ”๋กœ ๋‹ค์‹œ ์‚ด ์ˆ˜ ์žˆ๋‹ค๋Š” ์กฐ๊ฑด์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅ ํ•œ ์ผ์ด๋‹ค

๋˜ ๊ฐ’์ด ๋‚ด๋ ค๊ฐ„๋‹ค๋ฉด ํŒ”์ง€ ์•Š์„ ๊ฒƒ์ด๋‹ˆ๊นŒ ์˜ฌ๋ผ๊ฐˆ๋•Œ ๊นŒ์ง€ ๋Œ€๊ธฐํƒ„๋‹ค
๊ณ„์† ๋‚ด๋ ค๊ฐ€๋‹ค๊ฐ€ ๊ฐ’์ด ์˜ฌ๋ผ๊ฐ„๋‹ค๋ฉด ๊ณ„์† ๋‚ด๋ ค๊ฐ„ ๋งˆ์ง€๋ง‰ ๊ฐ’์ด ์ œ์ผ ์ž‘์€ ๊ฐ’์ด๋ฏ€๋กœ ๊ฑฐ๊ธฐ์„œ ์‚ฌ์„œ ๋‹ค์Œ๋‚  ํŒ”๋ฉด ๊ทธ๊ฒŒ max profit์ด๋‹ค

๊ทธ๋Ÿฌ๋ฏ€๋กœ ํ•˜๋ฃจ ์ฐจ์ด๋กœ ์ฃผ์‹์„ ์‚ฌ๊ณ ํŒ”๊ณ ๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ positive number๋ฉด profit์— ๊ณ„์† ๋”ํ•ด์ฃผ๋ฉด ๋œ๋‹ค

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int profit = 0;
        int n = prices.size();

        for (int i = 1; i < n; i++) {
            profit += max(0, prices[i] - prices[i-1]);
        }

        return profit;
    }
};

๐Ÿ’ก Best Time to Buy and Sell Stock with Cooldown

You are given an array prices where prices[i] is the price of a given stock on the ith day.

Find the maximum profit you can achieve. You may complete as many transactions as you like (i.e., buy one and sell one share of the stock multiple times) with the following restrictions:

  • After you sell your stock, you cannot buy stock on the next day (i.e., cooldown one day).

Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).

Example 1:

Input: prices = [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]

Example 2:

Input: prices = [1]
Output: 0

Solution

Consider the different profits if we buy, sell, or cooldown on Day i.

Use 3 variables to track max profits if our last action is to buy, sell, or cooldown TILL Day i.

Be careful about what action we took TILL Day i-1,
if we choose to do certain actions ON Day i.

  • Buy today
    โ†’ the last action till yesterday MUST BE cooldown
    compare: cooldown yesterday and buy today vs. max profit yesterday when the last action is to buy (keeping 'buy' status)

  • Sell today
    โ†’ the last action till yesterday MUST BE buy
    compare: sell the stock based on the last buy vs. max profit yesterday when the last action is to sell (keeping 'sell' status)

  • Cooldown today
    โ†’ the last action till yesterday MUST BE sell OR cooldown
    compare: max profit yesterday when the last actioin is sell vs. max profit yesterday when the last action is cooldown

We always need to check if we decide to buy/sell/cooldown today, can we earn more profit than yesterday, if not, we will give up and remain the max profit we met so far.

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        if (prices.size() <= 1) return 0;

        // max profit value for each of the actions from yesterday
        int cooled = 0;
        int sold = 0;
        int bought = 0;

        //on the first day, the only choice is to buy
        bought -= prices[0];
        
        for (int i = 1; i < prices.size(); i++) {
            int cooled_temp = cooled;
            int sold_temp = sold;
            int bought_temp = bought;

            bought = max(cooled_temp - prices[i], bought_temp);
            sold = max(bought_temp + prices[i], sold_temp);
            cooled = max(sold_temp, cooled_temp);
        }

        return max(sold, cooled); //only can do sell or cooldown on the lastday
    }
};

๐Ÿ’ก Word Break

Given a string s and a dictionary of strings wordDict, return true if s can be segmented into a space-separated sequence of one or more dictionary words.

Note that the same word in the dictionary may be reused multiple times in the segmentation.

Example 1:

Input: s = "leetcode", wordDict = ["leet","code"]
Output: true
Explanation: Return true because "leetcode" can be segmented as "leet code".

Example 2:

Input: s = "applepenapple", wordDict = ["apple","pen"]
Output: true
Explanation: Return true because "applepenapple" can be segmented as "apple pen apple".
Note that you are allowed to reuse a dictionary word.

Example 3:

Input: s = "catsandog", wordDict = ["cats","dog","sand","and","cat"]
Output: false

Solution

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        // store the words from dictionary to hash for constant lookup
        unordered_set<string> word_dict;
        for (int i = 0; i < wordDict.size(); i++) {
            word_dict.insert(wordDict[i]);
        }

        int n = s.size();
        vector<bool> dp(n + 1);
        // dp์—์„œ true๋ฅผ ๊ธฐ์ค€์œผ๋กœ new substring์„ ์‹œ์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— 
        // ์ฒซ๋ฒˆ์งธ letter์—์„œ true๋กœ ์ง€์ •ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค
        dp[0] = true;

        // let's process the string bottom up
        for (int i = 1; i <= n; i++) {
            for (int j = i-1; j >= 0; j--) {
                if (dp[j]) {
                    // dp[j] == true ์ด๋ฉด s[0:j]๊ฐ€ segmented ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๊ณ ,
                    //   s[0:j]์—์„œ ๋Š๊ณ ,
                    //   s[j:]๋ถ€ํ„ฐ matching์„ ์‹œ์ž‘ํ•ด์•ผ ๋œ๋‹ค!
                    string substring = s.substr(j, i - j); //substr(starting index, length)
                    if (word_dict.find(substring) != word_dict.end()) {
                        // if this substring exists in the dictionary
                        dp[i] = true; // meaning that s[j:i+1] exists in the dict
                        break;
                    }
                }
            }
        }

        return dp[n];
    }
};

๐Ÿ’ก Coin Change

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.

You may assume that you have an infinite number of each kind of coin.

Example 1:

Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1

Example 2:

Input: coins = [2], amount = 3
Output: -1

Example 3:

Input: coins = [1], amount = 0
Output: 0

Solution

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        // each index in dp represents the remainder
        // dp[i] will represent minimum # of coins to fulfill the corresponding remainder amount
        vector<int> dp(amount + 1, amount + 1); // initialize #coins to max number
        										// cannot initialize with INT_MAX, 
                                                // because we perfrom + 1 operation when taking min

        // to fulfill 0 cost, we need 0 coins
        dp[0] = 0;

        for (int i = 1; i <= amount; i++) {
            for (int c = 0; c < coins.size(); c++) {
                if (i >= coins[c]) { //if this coin fits into remainder amount
                    // update current dp such that is the minimum between
                    //   the current dp
                    //    or
                    //   when using this coin
                    dp[i] = min(dp[i], dp[i - coins[c]] + 1);
                }
            }
        }

        if (dp[amount] == amount + 1) return -1;
        return dp[amount];


    }
};

๐Ÿ’ก Coin Change II

You are given an integer array coins representing coins of different denominations and an integer amount representing a total amount of money.

Return the number of combinations that make up that amount. If that amount of money cannot be made up by any combination of the coins, return 0.

Example 1:

Input: amount = 5, coins = [1,2,5]
Output: 4
Explanation: there are four ways to make up the amount:
5=5
5=2+2+1
5=2+1+1+1
5=1+1+1+1+1

Example 2:

Input: amount = 3, coins = [2]
Output: 0
Explanation: the amount of 3 cannot be made up just with coins of 2.

Solution

class Solution {
public:
    int change(int amount, vector<int>& coins) {
        int n = coins.size();

        // sort coins vector
        sort(coins.begin(), coins.end());

        vector<vector<int>> dp(amount + 1, vector<int>(n));

        // initialize the basecase
        for (int j = 0; j < n; j++) {
            dp[0][j] = 1;
        }

        for (int i = 1; i <= amount; i++) {
            for (int j = n - 1; j >= 0; j--) {
                if (coins[j] > i) {
                    dp[i][j] = 0;
                } else {
                    dp[i][j] += dp[i - coins[j]][j]; // comb. using this coin
                    if (j != n - 1) 
                        dp[i][j] += dp[i][j+1]; // ALL the other comb. w/o using this coin
                }
            }
        }

        return dp[amount][0];

    }
};

๐Ÿ’ก Decode Ways

Given a string w/ only digits, return # ways to decode it (letter โ†’ digit)

s = "12" -> 2 (AB 1 2 or L 12)
s = "226" -> 3 (2 26 or 22 6 or 2 2 6)

class Solution {
public:
    int numDecodings(string s) {
        if (s[0] == '0') {
            return 0;
        }
        
        int n = s.size();
        
        vector<int> dp(n + 1);
        dp[0] = 1;
        dp[1] = 1;
        
        for (int i = 2; i <= n; i++) {
            int ones = stoi(s.substr(i - 1, 1));
            if (ones > 0) {
                dp[i] += dp[i - 1];
            }
            int tens = stoi(s.substr(i - 2, 2));
            if (tens >= 10 && tens <= 26) {
                dp[i] += dp[i - 2];
            }
        }
        
        return dp[n];
    }
};

๐Ÿ’ก Partition Equal Subset Sum

Given a non-empty array nums containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.

Example 1:

Input: nums = [1,5,11,5]
Output: true
Explanation: The array can be partitioned as [1, 5, 5] and [11].

Example 2:

Input: nums = [1,2,3,5]
Output: false
Explanation: The array cannot be partitioned into equal sum subsets.

Solution

์ผ๋‹จ nums๊ฐ€ 2๊ฐœ์˜ ๊ฐ™์€ ํ•ฉ์„ ๊ฐ€์ง„ subset์œผ๋กœ ๋‚˜๋ˆ ์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธ์ด ํ•„์š”ํ•˜๋‹ค
โ†’ ๋ชจ๋“  ์ˆซ์ž๋ฅผ ๋‹ค ๋”ํ–ˆ์„ ๋•Œ(=sum) ์ผ๋‹จ ์ง์ˆ˜๋ผ๋ฉด ๊ฐ€๋Šฅ์„ฑ์€ ์žˆ๋Š”๊ฒƒ
(ํ™€์ˆ˜๋ผ๋ฉด ๋ฐ”๋กœ return false, ๋‘๊ฐœ๋กœ ๋‚˜๋ˆ ์งˆ ์ˆ˜ ์—†๋‹ค)

์ด์ œ ๊ตฌํ•œ sum์„ ์ด๋“ฑ๋ถ„ํ•œ ๊ฐ’์„ target์œผ๋กœ ์ •ํ•ด๋‘๊ณ ,
nums์˜ ์ˆซ์ž๋“ค๋กœ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ๊ฐ€๋Šฅํ•œ subset์˜ ๊ฐ๊ฐ์˜ sum์„ ๊ตฌํ•œ๋‹ค
์ด์ค‘์— target์ด ์žˆ๋‹ค๋ฉด true, ์•„๋‹ˆ๋ผ๋ฉด false๋ฅผ returnํ•œ๋‹ค

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int target = 0;
        for (int i = 0; i < nums.size(); i++) {
            target += nums[i];
        }

        if (target % 2 == 1) 
            return false;
        
        target /= 2;

        // now find each and every subset's sum
        // and store into hashset
        unordered_set<int> subset_sums;
        subset_sums.insert(0);

        for (int i = 0; i < nums.size(); i++) {
            unordered_set<int> _subset_sums;
            
            for (auto it = subset_sums.begin(); it != subset_sums.end(); it++) {
                if (*it + nums[i] == target) {
                    // the target reached
                    return true;
                }
                // for each of the subset sums generated so far, 
                // we will add nums[i] to those subset sums 
                // to store the subset sums using nums[i]
                _subset_sums.insert(*it + nums[i]);
                _subset_sums.insert(*it); // since we are generating new _subset_sum
                                          // need to do this because of the for loop on subset_sum
            }
            subset_sums = _subset_sums;
        }

        // when here, target sum wasn't created in the subset sum
        return false;

    }
};

๐Ÿ’ก Longest Common Subsequence

Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.

Example 1:

Input: text1 = "abcde", text2 = "ace" 
Output: 3  
Explanation: The longest common subsequence is "ace" and its length is 3.

Example 2:

Input: text1 = "abc", text2 = "abc"
Output: 3
Explanation: The longest common subsequence is "abc" and its length is 3.

Example 3:

Input: text1 = "abc", text2 = "def"
Output: 0
Explanation: There is no such common subsequence, so the result is 0.

Solution

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int n = text1.size();
        int m = text2.size();
        vector<vector<int>> dp(n+1, vector<int>(m+1)); //+1 for the basecase
        
        for (int i = n - 1; i >= 0; i--) {
            for (int j = m - 1; j >= 0; j--) {
                if (text1[i] == text2[j])
                    dp[i][j] = dp[i+1][j+1] + 1;
                else
                    dp[i][j] = max(dp[i+1][j], dp[i][j+1]);
            }
        }

        return dp[0][0];
    }
};

๐Ÿ’ก Target Sum

You are given an integer array nums and an integer target.

You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.

For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1".
Return the number of different expressions that you can build, which evaluates to target.

Example 1:

Input: nums = [1,1,1,1,1], target = 3
Output: 5
Explanation: There are 5 ways to assign symbols to make the sum of nums be target 3.
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

Example 2:

Input: nums = [1], target = 1
Output: 1

Solution

ํ˜„์žฌ ์ˆซ์ž๋ฅผ ๋”ํ•˜๊ฑฐ๋‚˜ ๋นผ๊ฑฐ๋‚˜ ๋‘๊ฐ€์ง€ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๊ณ  ์ฝ”๋”ฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๋‹ค

๊ทธ๋ ‡๊ฒŒ ์ˆซ์ž๋ฅผ ๋”ํ•˜๊ฑฐ๋‚˜ ๋บ์„ ๋•Œ ๋‚˜์˜ค๋Š” total์„ ๊ฐ™์ด ๋ณด๊ด€ํ•˜์ž

{index, total} โ†’ # of ways
nums[index]๋ฅผ ํฌํ•จ ํ–ˆ์„ ๋•Œ์™€ ์•ˆํฌํ•จ ํ–ˆ์„ ๋•Œ ์ด๋ ‡๊ฒŒ ๋‘๊ฐ€์ง€๋กœ map์—๋Š” ๊ฐ™์€ index๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์• ๋“ค์ด ๋‘๊ฐœ๊ฐ€ ์žˆ๊ฒŒ ๋œ๋‹ค.

base case:

  • Valid: Index is out of bounds AND current sum is equal to target 'S'
  • Invalid: Index is out of bounds
class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        return backtrack(nums, target, 0, 0);
    }
private:
    // {(index, total) -> # of ways}
    map<pair<int, int>, int> dp;
    
    int backtrack(vector<int>& nums, int target, int i, int total) {
        if (dp.find({i, total}) != dp.end()) {
            // if the value was already calculted
            return dp[{i, total}];
        }

        // basecase when we used all the numbers
        if (i == nums.size()) {
            // Valid: current total is equal to target -> this is one valid expression! -> return 1
            // Invlid: current total is NOT equal to target
            return total == target ? 1 : 0;
        }
        
        // calculate the number of expressions if we add this number
        int positive = backtrack(nums, target, i + 1, total + nums[i]);
        // calculate the number of expressions if we subtract this number
        int negative = backtrack(nums, target, i + 1, total - nums[i]);

        // store the result to the map
        dp[{i, total}] = positive + negative;       
        
        return dp[{i, total}];
    }
};

๐Ÿ’ก Longest Increasing Path

Solution

๋ชจ๋“  cell๋“ค ๋Œ๋ฉด์„œ ์œ„, ์•„๋ž˜, ์˜ค๋ฅธ์ชฝ, ์™ผ์ชฝ์—์„œ๋ถ€ํ„ฐ ์™”์„ ๋•Œ ์ค‘์— longest increasing path๋ฅผ ๋ถˆ๋Ÿฌ์™€์„œ
๋ฆฌํ„ดํ•œ๋‹ค.

dfs๋ฅผ ๊ณ„์† ๋Œ์ง€ ์•Š๋„๋ก cache (Dynamic Programming) ํ•œ๋‹ค!

class Solution {
public:
    int longestIncreasingPath(vector<vector<int>>& matrix) {
        int m = matrix.size();
        int n = matrix[0].size();
        
        int result = 0;
        
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                result = max(result, dfs(matrix, -1, i, j, m, n));
            }
        }
        
        return result;
    }
private:
    // {(i, j) -> longest increasing path at (i, j)}
    map<pair<int, int>, int> dp;
    
    int dfs(vector<vector<int>>& matrix, int prev, int i, int j, int m, int n) {
        // prev -> stores the value from where it came from
        if (i < 0 || i >= m || j < 0 || j >= n || matrix[i][j] <= prev) {
            // out of bound or 
            // current value is less than where it came from (we are looking for increasing path)
            return 0;
        }

        if (dp.find({i, j}) != dp.end()) {
            // if already performed use it 
            return dp[{i, j}];
        }
        
        // current cell count as 1
        int result = 1;
        // for every direction find the maximum path 
        result = max(result, 1 + dfs(matrix, matrix[i][j], i - 1, j, m, n));
        result = max(result, 1 + dfs(matrix, matrix[i][j], i + 1, j, m, n));
        result = max(result, 1 + dfs(matrix, matrix[i][j], i, j - 1, m, n));
        result = max(result, 1 + dfs(matrix, matrix[i][j], i, j + 1, m, n));
        dp[{i, j}] = result;
        
        return dp[{i, j}];
    }
};
profile
๋ฉ‹์ฐ ๋‚˜ ,,(๊ฐ€ ๋˜๊ณ ํ”ˆ)

0๊ฐœ์˜ ๋Œ“๊ธ€