Skip to content

Latest commit

ย 

History

History
443 lines (329 loc) ยท 9.99 KB

6_DynamicProgramming.md

File metadata and controls

443 lines (329 loc) ยท 9.99 KB

Dynamic Programming

  1. Problem์ด subproblem์œผ๋กœ ์ชผ๊ฐœ์งˆ ๋•Œ
  2. subproblem์˜ ์†”๋ฃจ์…˜์œผ๋กœ ๋” ํฐ problem์˜ ์†”๋ฃจ์…˜์„ ๊ตฌํ•  ์ˆ˜ ์žˆ์„๋•Œ
  3. subproblem๋“ค์ด ๊ฒน์น  ๋•Œ

  • memoization์„ ์‚ฌ์šฉํ•ด ๊ฒน์น˜๋Š” ๋ถ€๋ถ„์„ ๊ธฐ์–ตํ•œ๋‹ค.
  • Top-Down: ์žฌ๊ท€
  • Bottom-Up: ์ž‘์€ ๋ถ€๋ถ„๋ถ€ํ„ฐ

Min Cost Climbing Stairs

LeetCode 746. Min Cost Climbing Stairs

  • ์ด์ „ ๋‹จ๊ณ„๋ผ๋Š” subproblem์œผ๋กœ ๋‚˜๋ˆ ์„œ ์ƒ๊ฐํ•œ๋‹ค.
func minCostClimbingStairs(_ cost: [Int]) -> Int {

    let totalCount: Int = cost.count
    var minCost: [Int] = Array(repeating: 0, count: totalCount+1)

    for i in 2...totalCount {
        let oneStep: Int = minCost[i-1] + cost[i-1]
        let twoStep: Int = minCost[i-2] + cost[i-2]
        minCost[i] = min(oneStep, twoStep)
    }
    return minCost[totalCount]
}

Minimum Path Sum

LeetCode 64. Minimum Path Sum

  • ์ด์ „ ๋‹จ๊ณ„์ค‘ ์ตœ์†Œ ๋น„์šฉ์„ ์„ ํƒ
func minPathSum(_ grid: [[Int]]) -> Int {
    let rows: Int = grid.count
    let cols: Int =  grid[0].count
    var minCost2d: [[Int]] = Array(
        repeating: Array(repeating: 0, count: cols), 
        count: rows
    )

    minCost2d[0][0] = grid[0][0]
    for colIdx in 1..<cols { 
        minCost2d[0][colIdx] = grid[0][colIdx] + minCost2d[0][colIdx-1]
    }
    for rowIdx in 1..<rows { 
        minCost2d[rowIdx][0] = grid[rowIdx][0] + minCost2d[rowIdx-1][0]
    }
  
    for rowIdx in 1..<rows { 
        for colIdx in 1..<cols { 
            let preRow = rowIdx - 1
            let preCol = colIdx - 1
            let cost = min(
                minCost2d[preRow][colIdx],
                minCost2d[rowIdx][preCol]
            )
            minCost2d[rowIdx][colIdx] = cost + grid[rowIdx][colIdx]
        }
    }

    return minCost2d[rows-1][cols-1]
}

๋™์ „ ๋ฐ”๊พธ๊ธฐ

LeetCode 322. Coin Change

  • F(n) = Min(F(n-2), F(n-3), F(n-5) + 1
  • bottom up ๋ฐฉ์‹ ์„ ํ˜ธ
  • TC: O(K*n), (k: ๋™์ „ ๊ฐœ์ˆ˜)
func coinChange(_ coins: [Int], _ amount: Int) -> Int {

    var dp = Array(repeating: -1, count: amount + 1)
    dp[0] = 0

    for i in 0...amount { 
        if dp[i] != -1 { continue }

        var countMin: Int = Int.max
        for coin in coins { 
            let lastIdx = i - coin
            if lastIdx < 0 { continue }

            let lastCost = dp[lastIdx]
            if lastCost == -1 { continue }

            let cost = lastCost + 1
            countMin = min(cost, countMin)
        }
        dp[i] = countMin == Int.max ? -1 : countMin
    }
  
    return dp[amount]
}

Decoding Ways

LeetCode 91. Decode Ways

  • ๋ช‡๊ฐ€์ง€ ๋ฐฉ๋ฒ•? -> DP
    • sub problem์œผ๋กœ ๋‚˜๋ˆ  ๋ณธ๋‹ค.
      • topdown

      • bottomup ๋’ค์—์„œ๋ถ€ํ„ฐ ํ™•์ธ


func numDecodings(_ s: String) -> Int {
    let chars: [Character] = Array(s)
    let strCount: Int = s.count
    if strCount == 0 { return 0 }

    var dp: [Int] = Array(repeating: -1, count: strCount+1)
    dp[strCount] = 1

    let lastChar: Character = chars[chars.count-1]
    if Int(String(lastChar))! == 0 { 
        dp[strCount-1] = 0
    } else { 
        dp[strCount-1] = 1
    }

    for i in stride(from: strCount-2, through: 0, by: -1) { 
        let singleNum: Int = Int(String(chars[i]))!
        let singleCount: Int = 0 < singleNum ? dp[i+1] : 0

        let doubleNum: Int = Int(String(chars[i+1]))! + singleNum * 10
        let doubleCount: Int = 10...26 ~= doubleNum ? dp[i+2] : 0

        dp[i] = singleCount + doubleCount
    }

    return dp[0]
} 

Max SubArray Sum

LeetCode 53. Decode Ways

  • max subarray with last element๋กœ ์ƒ๊ฐ
  • F(n) = max(In(n), In(n)+F(n-1))
    • ๋งˆ์ง€๋ง‰, ๋งˆ์ง€๋ง‰ + ์ด์ „ ์ƒํ™ฉ

func maxSubArray(_ nums: [Int]) -> Int {
    var dp: [Int] = Array(repeating: -1, count: nums.count)
    dp[0] = nums[0]

    for i in 1..<nums.count { 
        dp[i] = max(nums[i], nums[i]+dp[i-1])
    }

    return dp.max() ?? -1
}

์ตœ๋Œ€ ๊ณฑ subArray

LeetCode 152.Maximum Product Subarray


func maxProduct(_ nums: [Int]) -> Int {
  
    var maxDP: [Int] = [nums[0]]
    var minDP: [Int] = [nums[0]]

    for idx in 1..<nums.count { 
        let prevIdx = idx - 1

        let num: Int = nums[idx]
        let cand0: Int = num
        let cand1: Int = maxDP[prevIdx]*num
        let cand2: Int = minDP[prevIdx]*num

        maxDP.append(max(cand0, cand1, cand2))
        minDP.append(min(cand0, cand1, cand2))
    }
    return maxDP.max() ?? -1
}

์ตœ๋Œ€ ๊ธธ์ด Palindrome Substring

  • 2์ฐจ์› dp
func longestPalindrome(s: String) -> String {
    let strCount: Int = s.count
    let chars: [Character] = Array(s)
    var dp: [[Int]] = Array(
        repeating: Array(repeating: 0, count: strCount),
        count: strCount
    )

    for i in 0..<strCount { 
        dp[i][i] = 1
    }

    for i in 1..<strCount-1 { 
        dp[i][i+1] = 2
    }

    for i in 2..<strCount { 
        var row: Int = 0
        var col: Int = i
        while col < strCount { 
            let startChar: Character = chars[row]
            let endChar: Character =  chars[col]
            let prevCount: Int = dp[row+1][col-1]
            if startChar == endChar && prevCount != 0 { 
                dp[row][col] = prevCount + 2
            }
            row += 1
            col += 1
        }
    }

    var maxLength = 0 
    var startIdx = 0
    var endIdx = 0
    for row in 0..<strCount {
        for col in 0..<strCount { 
            let currentLength: Int = dp[row][col]
            if maxLength < currentLength {
                maxLength = currentLength
                startIdx = row
                endIdx = col
            }
        }
    }

    let subStr: String = String(chars[startIdx...endIdx])
    return subStr
}

print(longestPalindrome(s: "baabc"))

LeetCode 647. Palindromic Substrings

func countSubstrings(_ s: String) -> Int {
    let s = Array(s)

    var dp = Array(
        repeatElement(Array(repeatElement(false, count: s.count)), 
        count: s.count)
    )
    var count = 0

    for i in 0..<s.count {
        dp[i][i] = true
        count += 1
    }

    for r in 1..<s.count { 
        for l in 0..<r { 
            if s[r] == s[l] && (dp[l+1][r-1] || r - l <= 2) { 
                dp[l][r] = true
                count += 1
            }
        }
    }

    return count
}

๋‹จ์–ด ์ž๋ฅด๊ธฐ

func wordBreak(s: String, wordDict: [String]) -> Bool { 
    let chars: [Character] = Array(s)
    let wordSet: Set<String> = Set(wordDict)

    let strCount: Int = s.count
    var dp: [Bool] = Array(repeating: false, count: strCount)
    dp[0] = true

    for idx in 1..<strCount { 
        for word in wordSet { 
            let wordLength: Int = word.count
            let prevIdx: Int = idx - wordLength
          
            if prevIdx < 0 { continue }
            if !dp[prevIdx] { 

                continue 
            }

            if String(chars[prevIdx..<idx]) == word {
                dp[idx] = true
                break
            }
        }
    }
    return dp.last!
}

knapsack problem

  • ๋ฐฐ๋‚ญ์•ˆ์— ์žˆ๋Š” ๊ฒฝ์šฐ ์—†๋Š” ๊ฒฝ์šฐ๋กœ subproblem ์ •์˜
  • NS(n, w) = max(NS(n-1, w), NS(n-1, w-w[n])+val[n]), (n - ๋ช‡๋ฒˆ์งธ๋ฌผ๊ฑด๊นŒ์ง€, w - ๋ฌด๊ฒŒ ๋ฆฌ๋ฐ‹)
    • 2๊ฐœ์˜ ๋ณ€์ˆ˜ -> 2์ฐจ์› dp
  • TC: O(nw)
  • SC: O(nw)

0 1 2 3 4 5
0 0 0 0 0 0
A 0 30 30 30 30 30
AB 0 30 30 50 50 50
ABC 0 30 30 50 70 70
ABCD 0 30 30 50 70 70

Partition Equal Subset Sum

LeetCode 416.Partition Equal Subset Sum

  • (์ดํ•ฉ / 2) subset ์žˆ๋Š”์ง€ ํ™•์ธ
  • knapsack problem ์ฒ˜๋Ÿผ ์žˆ๋Š” ๊ฒฝ์šฐ ์—†๋Š” ๊ฒฝ์šฐ๋กœ ๋‚˜๋ˆ„์–ด ์ƒ๊ฐํ•œ๋‹ค.
  • DP(n, s) = DP(n-1, s) || DP(n-1, s-In[n])
  • TC: O(ns)
  • SC: O(ns)

[2, 1, 2, 3, 4]

0 1 2 3 4 5 6
T F F F F F F
2 T F T F F F F
21 T T T T F F F
212 T T T T T T F
2123 T T T T T T T
21234 T T T T T T T

Coin Change 2

  • knapsack ์ฒ˜๋Ÿผ ์„ ํƒ๋œ๊ฒฝ์šฐ ์„ ํƒ๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ๋กœ ๋‚˜๋ˆ„์–ด ์ƒ๊ฐํ•œ๋‹ค.
  • dp(n, s) = dp(n-1, s) + dp(n, s - coin)
  • TC: O(ns)
  • TS: O(ns)

[1, 2, 3], sum = 5

0 1 2 3 4 5
1 0 0 0 0 0
1 1 1 1 1 1 1
1,2 1 1 2 2 3 3
1,2,3 1 1 2 3 4 5
func coinChange(amount: Int, coins: [Int]) -> Int {

    var dp: [[Int]] = Array(
        repeating: Array(repeating: 0, count: amount+1), 
        count: coins.count+1
    )

    for i in dp.indices { 
        dp[i][0] = 1 
    }

    for rowIdx in 1..<coins.count+1 {
        for colIdx in 1..<amount+1 {
            let prevRowIdx: Int = rowIdx - 1
            let coin: Int = colIdx - coins[rowIdx-1] 
            var sum: Int = dp[prevRowIdx][colIdx]
            if coin >= 0 { 
                sum += dp[rowIdx][coin]
            }
            dp[rowIdx][colIdx] = sum
        }
    }

    print(dp)

    return dp[coins.count][amount]
}

Longest Common Subsequence

  • TC: O(mn)
  • SC: O(mn)


์ฐธ๊ณ