<p>You are given <code>n</code> balloons, indexed from <code>0</code> to <code>n - 1</code>. Each balloon is painted with a number on it represented by an array <code>nums</code>. You are asked to burst all the balloons.</p>

<p>If you burst the <code>i<sup>th</sup></code> balloon, you will get <code>nums[i - 1] * nums[i] * nums[i + 1]</code> coins. If <code>i - 1</code> or <code>i + 1</code> goes out of bounds of the array, then treat it as if there is a balloon with a <code>1</code> painted on it.</p>

<p>Return <em>the maximum coins you can collect by bursting the balloons wisely</em>.</p>

<p>&nbsp;</p>
<p><strong class="example">Example 1:</strong></p>

<pre>
<strong>Input:</strong> nums = [3,1,5,8]
<strong>Output:</strong> 167
<strong>Explanation:</strong>
nums = [3,1,5,8] --&gt; [3,5,8] --&gt; [3,8] --&gt; [8] --&gt; []
coins =  3*1*5    +   3*5*8   +  1*3*8  + 1*8*1 = 167</pre>

<p><strong class="example">Example 2:</strong></p>

<pre>
<strong>Input:</strong> nums = [1,5]
<strong>Output:</strong> 10
</pre>

<p>&nbsp;</p>
<p><strong>Constraints:</strong></p>

<ul>
	<li><code>n == nums.length</code></li>
	<li><code>1 &lt;= n &lt;= 300</code></li>
	<li><code>0 &lt;= nums[i] &lt;= 100</code></li>
</ul>

import numpy as np
class Solution:
    def maxCoins(self, nums: List[int]) -> int:
        # dp[i][j]
        n = len(nums)
        dp = np.zeros((n, n)).astype(int)
        for i in range(n):
            dp[i][i] = nums[i]
            if i > 0: dp[i][i] *= nums[i-1]
            if i < n-1: dp[i][i] *= nums[i+1]
        for i in range(1, n):
            for j in range(n-i):
                # dp[j][j+i]
                for z in range(j, i+j+1):
                    out = nums[z]
                    if j>0: out *= nums[j-1]
                    if i+j+1<n: out *= nums[i+j+1]
                    if z>j: out += dp[j][z-1]
                    if z<i+j: out += dp[z+1][i+j]
                    dp[j][j+i] = max(dp[j][j+i], out)
        return dp[0][n-1]
                
























# # class Solution:
# #     def maxCoins(self, nums: List[int]) -> int:
# #         dp = {}

# #         def brustBallons(ballons):
# #             if len(ballons)==1:
# #                 return ballons[0]
# #             if tuple(ballons) in dp:
# #                 return dp[tuple(ballons)]
# #             ans = 1
# #             for i in range(len(ballons)):
# #                 ballon = ballons.pop(i)
# #                 res = brustBallons(ballons)
# #                 ballons.insert(i, ballon)
# #                 if i == 0:
# #                     res+=ballons[0]*ballons[1]
# #                 elif i == len(ballons)-1:
# #                     res+=ballons[-1]*ballons[-2]
# #                 else:
# #                     res+=ballons[i]*ballons[i-1]*ballons[i+1]
# #                 ans = max(ans, res)
# #             dp[tuple(ballons)]=ans
# #             return ans
        
# #         return brustBallons(nums)

# class Solution:
#     def maxCoins(self, nums: List[int]) -> int:
#         dp = {}
#         n=len(nums)
#         nums = [1] + nums + [1] # add edge

#         def burstBallons(left, right):
#             if left > right:
#                 return 0
#             if left==right:
#                 return nums[left-1]*nums[left]*nums[left+1]
#             if (left, right) in dp:
#                 return dp[(left, right)]
#             ans = 0
#             for i in range(left, right+1): # last burst
#                 res = burstBallons(left, i-1) + burstBallons(i+1, right) + nums[left-1]*nums[i]*nums[right+1]
#                 ans = max(ans, res)
#             dp[(left, right)]=ans
#             return ans
        
#         return burstBallons(1, n)

26874