def rob(nums: List[int]) -> int:
# if there is two houses or less just return the amount of the house with maximum amount
if len(nums) <= 2:
return(max(nums))
# initialize two list with size equal the number of houses
dp = [[0 for _ in range(len(nums))] for _ in range(2)]
dp[0][0], dp[0][1] = nums[0], max(nums[0], nums[1]) # first list where first house can be robbed (and last house can't)
dp[1][0], dp[1][1] = 0, nums[1] # second list where first house can't be robbed (and last house can)
# loop over each house starting with 3rd one until penultimate house
for i in range(2, len(nums)-1):
dp[0][i] = max(dp[0][i-1], dp[0][i-2] + nums[i]) # maximum amount is max between precendent house amount and i-2 house amount + current house amount
dp[1][i] = max(dp[1][i-1], dp[1][i-2] + nums[i]) # maximum amount is max between precendent house amount and i-2 house amount + current house amount
dp[0][-1] = dp[0][-2] # in first list last house can't be robbed so its amount is the same as penultimate house
dp[1][-1] = max(dp[1][-2], dp[1][-3] + nums[-1]) # in the second list we can apply the classic method
# max amount is the max of the two strategies: 1) rob first and not last and 2) rob last and not first
return(max(dp[0][-1], dp[1][-1]))