<p>You are given an array of integers <code>stones</code> where <code>stones[i]</code> is the weight of the <code>i<sup>th</sup></code> stone.</p>

<p>We are playing a game with the stones. On each turn, we choose any two stones and smash them together. Suppose the stones have weights <code>x</code> and <code>y</code> with <code>x &lt;= y</code>. The result of this smash is:</p>

<ul>
	<li>If <code>x == y</code>, both stones are destroyed, and</li>
	<li>If <code>x != y</code>, the stone of weight <code>x</code> is destroyed, and the stone of weight <code>y</code> has new weight <code>y - x</code>.</li>
</ul>

<p>At the end of the game, there is <strong>at most one</strong> stone left.</p>

<p>Return <em>the smallest possible weight of the left stone</em>. If there are no stones left, return <code>0</code>.</p>

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

<pre>
<strong>Input:</strong> stones = [2,7,4,1,8,1]
<strong>Output:</strong> 1
<strong>Explanation:</strong>
We can combine 2 and 4 to get 2, so the array converts to [2,7,1,8,1] then,
we can combine 7 and 8 to get 1, so the array converts to [2,1,1,1] then,
we can combine 2 and 1 to get 1, so the array converts to [1,1,1] then,
we can combine 1 and 1 to get 0, so the array converts to [1], then that&#39;s the optimal value.
</pre>

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

<pre>
<strong>Input:</strong> stones = [31,26,33,21,40]
<strong>Output:</strong> 5
</pre>

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

<ul>
	<li><code>1 &lt;= stones.length &lt;= 30</code></li>
	<li><code>1 &lt;= stones[i] &lt;= 100</code></li>
</ul>

# Algorithm
# 1. If you have two piles of stones: A & B
#    You can smash stones to get |sum(A) - sum(B)| in the end.
#
#    ex) A: a1, a2, a3, a4
#        B: b1, b2, b3
#        -------------------------------------------
#        A: a1+a2+a3+a4 - b1
#        B: b2+b3
#        ------------------- ... -------------------
#        A: 0
#        B: b1+b2+b3 - (a1+a2+a3+a4)           
#
#  2. Reversely, depending on the last stone weight you want to get,
#     you can divide stones into two piles (A & B) and get the weight you want.
# 
#  3. Now, if you can get a pile that has weight closest to the half-sum.
#     You can get the smallest possible weight in the end.
#
#  4. define f(i, built)
#     - i: index of the current stone.
#     - built: sum of piles you have built so far
#     - if built < target, you can include i or not include i.
#     - else, return the difference. (it is the last stone weight)
#
class Solution:
    def lastStoneWeightII(self, stones: List[int]) -> int:
        # if even, half will be exact half.
        # if odd, piles will be 'floor' and 'ceiling'
        whole = sum(stones)
        half = math.floor(whole / 2)

        cache = {}
        def f(i, built):
            # finished
            if len(stones) <= i or half <= built:
                return built
            # use i or not.
            else:
                if (i, built) in cache:
                    return cache[(i, built)]

                opt1 = f(i+1, built + stones[i])
                opt2 = f(i+1, built)

                result = opt1 if abs(half - opt1) <= abs(half - opt2) else opt2
                cache[(i, built)] = result

                return result
        
        a = f(0,0)
        b = whole - a
        return abs(a-b)
22500