<p>You are given two <strong>0-indexed</strong> integer arrays,&nbsp;<code>cost</code> and <code>time</code>, of size <code>n</code> representing the costs and the time taken to paint <code>n</code> different walls respectively. There are two painters available:</p>

<ul>
	<li>A<strong>&nbsp;paid painter</strong>&nbsp;that paints the <code>i<sup>th</sup></code> wall in <code>time[i]</code> units of time and takes <code>cost[i]</code> units of money.</li>
	<li>A<strong>&nbsp;free painter</strong> that paints&nbsp;<strong>any</strong> wall in <code>1</code> unit of time at a cost of <code>0</code>. But the&nbsp;free painter can only be used if the paid painter is already <strong>occupied</strong>.</li>
</ul>

<p>Return <em>the minimum amount of money required to paint the </em><code>n</code><em>&nbsp;walls.</em></p>

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

<pre>
<strong>Input:</strong> cost = [1,2,3,2], time = [1,2,3,2]
<strong>Output:</strong> 3
<strong>Explanation:</strong> The walls at index 0 and 1 will be painted by the paid painter, and it will take 3 units of time; meanwhile, the free painter will paint the walls at index 2 and 3, free of cost in 2 units of time. Thus, the total cost is 1 + 2 = 3.
</pre>

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

<pre>
<strong>Input:</strong> cost = [2,3,4,2], time = [1,1,1,1]
<strong>Output:</strong> 4
<strong>Explanation:</strong> The walls at index 0 and 3 will be painted by the paid painter, and it will take 2 units of time; meanwhile, the free painter will paint the walls at index 1 and 2, free of cost in 2 units of time. Thus, the total cost is 2 + 2 = 4.
</pre>

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

<ul>
	<li><code>1 &lt;= cost.length &lt;= 500</code></li>
	<li><code>cost.length == time.length</code></li>
	<li><code>1 &lt;= cost[i] &lt;= 10<sup>6</sup></code></li>
	<li><code>1 &lt;= time[i] &lt;= 500</code></li>
</ul>

class Solution:
    def dfs(self, cur: int, t: int) -> int:
        if cur >= len(self.cost):
            return 0
        elif t >= len(self.cost)-cur:
            return 0
        elif (cur, t) in self.cache:
            return self.cache[(cur,t)]
        else:
            # 1. use paid painter
            res1 = self.cost[cur] + self.dfs(cur+1, t+self.time[cur])
            # 2. if t+sum(self.time[cur+1:]) >= 0, we can use free painter
            # Otherwise, we cannot use
            if t-1+self.query_sum(cur+1, len(self.time)-1) >= 0:
            # if t-1+sum(self.time[cur+1:]) >= 0:
                res2 = self.dfs(cur+1, t-1)
            else:
                res2 = self.INF
            self.cache[(cur, t)] = min(res1, res2)
            return self.cache[(cur, t)]
    
    def build_prefix(self) -> List[int]:
        prefix_sum = [0]*len(self.time)
        for i in range(len(self.time)):
            if i==0:
                prefix_sum[i] = self.time[i]
            else:
                prefix_sum[i] = self.time[i]+prefix_sum[i-1]
        return prefix_sum
    
    def query_sum(self, start: int, end: int) -> int:
        if start == 0:
            return self.prefix_sum[end]
        else:
            return self.prefix_sum[end] - self.prefix_sum[start-1]
    
    def paintWalls(self, cost: List[int], time: List[int]) -> int:
        self.cache = {}
        self.cost = cost
        self.time = time
        self.INF = 10**9
        self.prefix_sum = self.build_prefix()
        res = self.dfs(0,0)
        return res
2728