class Solution {
public int paintWalls(int[] cost, int[] time) {
int n = cost.length;
// Create a DP table where:
// dp[i][remain] represents the minimum cost required to "unlock" painting for
// 'remain' number of walls when considering walls from index i to n-1.
int[][] dp = new int[n + 1][n + 1];
// Base case initialization:
// When we've processed all walls (i = n), if remain > 0 then it's impossible to cover
// the required walls because there are no more walls to use for unlocking free painting.
// Hence, we set dp[n][remain] to a large number (in this case, 1e9).
// dp[n][0] is implicitly 0 since no further cost is needed if no walls remain.
for (int i = 1; i <= n; i++) {
dp[n][i] = (int) 1e9;
}
// Process walls in reverse order (from last wall to first).
// This way, we build up the solution using the results of future decisions.
for (int i = n - 1; i >= 0; i--) {
// For each possible number of walls that still need to be "unlocked" for free painting.
// 'remain' represents the number of walls that need to be painted (unlocked).
for (int remain = 1; remain <= n; remain++) {
// Option 1: Use the paid painter for wall i.
// The cost incurred is cost[i].
// The paid painter takes time[i] units of time to paint wall i.
// During that time, the free painter can paint time[i] walls for free.
// Since wall i is also painted by the paid painter, we reduce the remaining count by 1 + time[i].
// We use Math.max to ensure the remaining count doesn't go below zero.
int paint = cost[i] + dp[i + 1][Math.max(0, remain - 1 - time[i])];
// Option 2: Skip using the paid painter for wall i.
// In this case, no cost is incurred, but the number of walls that need to be covered remains the same.
int dontPaint = dp[i + 1][remain];
// The DP state dp[i][remain] is the minimum of the two options.
dp[i][remain] = Math.min(paint, dontPaint);
}
}
// The final answer is at dp[0][n], meaning starting from the first wall
// with a requirement to cover all n walls.
return dp[0][n];
}
}