LeetCode-Problems / 0256. Paint House / 0256. Paint House.py
0256. Paint House.py
Raw
# First thing that comes to mind is a classic binary tree.
# Also, maybe an intro DP problem and I just dont know enough
# about DP to work through it. Anyhow, if you make an arbitrary
# choice for the first house on one end (the root), the next house
# will be left with just two choices, which can each be child nodes.
# Each of those have only 2 choices.... repeat til you get to the last
# house. Each node contains info on the color of that house, and total
# costs accumulated so far. Repeat this process for all 3 initial choices.
# It will work, but seems pretty redundant... Ill start with it and see
# if it passes.

# Wait, I'm an idiot. There are up to 100 houses, which means this would be
# a b-tree with a height of 100. 2^100 entries is going to be... well "a bit"
# I dont think this method is going to work at all. We are adding the same
# values over and over. This is what memoization is for. I really need to
# wrap my head around DP and memoization, its brilliant, relatively easy,
# and super critical to problems like this.


# Ok, reading my way through the brilliant solution document it gives a
# psuedocode block for its 3rd solution, recursion with memoization.
# This is an attempt to code that without lookin at the actual code

# Nice! It works and is just utterly straightforward. Simple obvious recursion
# but store the results in memo dict. Ive commented out the manual memo
# to try the LRU cache for automatic memoization. It works. Sweet
# class Solution:
#    def minCost(self, costs: List[List[int]]) -> int:
#         self.last_house = len(costs) - 1
#         # self.memo = {}           #manual memo, a dict

#         @lru_cache(maxsize=None)
#         def paint(n,color):
#             # if (n, color) in self.memo:
#             #     return self.memo[(n,color)]      #tuples can be keys as they are immutable
#             total_cost = costs[n][color]
#             if n == self.last_house:
#                 pass                        #exits the if block
#             elif color == 0:
#                 total_cost += min(paint(n+1,1),paint(n+1,2))
#             elif color == 1:
#                 total_cost += min(paint(n+1,2),paint(n+1,0))
#             elif color == 2:
#                 total_cost += min(paint(n+1,0),paint(n+1,1))
#             # self.memo[(n,color)] = total_cost
#             return total_cost

#         return min(paint(0,0), paint(0,1), paint(0,2))

# And so lastly we try to follow the DP solution. DP here is iterative, so beats memo recursion
# by a long shot. We start a DP by identifying a subproblem: Calculate the cost for a particular
# house (position/number) given a color. The key here is actually writing in the cacluclated costs
# directly into our input matrix, so we use that result for input in the future.

# My goodness... thats absurdly simple. The last row is the cost of painting the last house... so
# for each input in the row above it, simply pick the min of the remaining choices in the row below,
# and literally add it to the element in this row. Repeat. The choice propogates up. Its amazing once
# youve seen the directed graph diagram overlayed onto the input matrix.


class Solution:
    def minCost(self, costs: List[List[int]]) -> int:
        cycle = [0, 1, 2]

        for row in range(len(costs) - 2, -1, -1):
            for color in range(len(costs[row])):
                costs[row][color] += min(
                    costs[row + 1][cycle[color - 1]], costs[row + 1][cycle[color - 2]]
                )

        return min(costs[0])


# SCRAP THIS DUMMY HIGH TREE IDEA

# class TreeNode:
#     def __init__(self, color, cost):
#         self.color = color
#         self.running_total = cost
#         self.next_one = None
#         self.right_two = None

# class Solution:
#    def minCost(self, costs: List[List[int]]) -> int:
#     cycle = [0,1,2]
#     for x in cycle:
#         root = TreeNode(x, costs[0][x])         #start a b-tree with each of the colors for house 0

#         curr_node = root
#         for i in len(costs):
#             curr_node.next_one = TreeNode(cycle[curr_node.color-1], curr_node.running_total + )