So I have been really trying to grasp Dynamic Programming. I can say that I really understand the memoization top down approach, but the bottoms up approach is really confusing to me. I was able to solve rods cutting top down, but I had to seek the solution for the bottoms up. I just don't understand when to use a 1D array or a 2D array. Then the for loop within the bottoms up is just confusing. Can anyone help me understand the differences in these two codes conceptually?
// Top Down Memoizaton: 
const solveRodCuttingTop = function(lengths, prices, n) {
  return solveRodCuttingHelper(0, lengths, prices, n); 
};
function solveRodCuttingHelper(idx, span, prices, n, memo = []) {
  // BASE CASES 
  if (idx === span.length || n <= 0 || prices.length !== span.length) {
    return 0;
  }
  let included = 0, excluded = 0; 
  memo[idx] = memo[idx] || []; 
  if (memo[idx][n] !== undefined) return memo[idx][n]; 
  if (span[idx] <= n) {
    included = prices[idx] + solveRodCuttingHelper(idx, span, prices, n - span[idx], memo);
  }
  excluded = solveRodCuttingHelper(idx + 1, span, prices, n, memo);
  memo[idx][n] = Math.max(included, excluded); 
  
  return memo[idx][n];
}
// Bottoms up 
const solveRodCuttingBottom = function(lengths, prices, n) {
  const rods = Array.from({length: n + 1});
  rods[0] = 0; 
  let maxRevenue = - Infinity;
  for (let i = 1; i < rods.length; i++) {
    for (let j = 1; j <= i; j++) {
      maxRevenue = Math.max(maxRevenue, prices[j - 1] + rods[i - j])
    }
    rods[i] = maxRevenue
  }
  return rods[prices.length];
};
const lengths = [1, 2, 3, 4, 5];
const prices = [2, 6, 7, 10, 13]; 
    