Back to posts

LeetCode Challenge Day 26 β€” 3186. Maximum Total Damage With Spell Casting

Nitin Ahirwal / October 11, 2025

LeetCode ChallengeDay 26Dynamic ProgrammingBinary SearchJavaScriptMediumPortfolio

Hey folks

This is Day 26 of my LeetCode streak πŸš€.
Today’s problem is 3186. Maximum Total Damage With Spell Casting β€” a dynamic programming problem where we must pick spells for maximum damage but avoid choosing ones too close in value.


πŸ“Œ Problem Statement

You are given:

  • power[i] β†’ the damage of the i-th spell.

Rules:

  • Each spell can be cast only once.
  • If you cast a spell with value x, then x-2, x-1, x+1, x+2 are all banned.

Return the maximum total damage possible.

Examples

  • Input:
    power = [1,1,3,4]
    Output: 6

  • Input:
    power = [7,1,6,6]
    Output: 13


πŸ’‘ Intuition

This is like the Delete and Earn problem.

  • First, group all spells with the same value.
  • Choosing a number blocks numbers within 2 distance.
  • So we only pick numbers at least 3 apart.

πŸ”‘ Approach

  1. Count total damage for each unique value.
  2. Sort these values.
  3. Use DP:
    • Skip current β†’ take best from previous.
    • Take current β†’ add its total + best from last allowed value (value ≀ current-3).
  4. Use binary search to find the last valid index quickly.

⏱️ Complexity Analysis

  • Time complexity: O(n + m log m) where m = unique values.
  • Space complexity: O(m).

πŸ§‘β€πŸ’» Code (JavaScript)

/**
 * @param {number[]} power
 * @return {number}
 */
var maximumTotalDamage = function(power) {
  const total = new Map();
  for (const v of power) total.set(v, (total.get(v) || 0) + v);

  const vals = Array.from(total.keys()).sort((a, b) => a - b);
  const sums = vals.map(v => total.get(v));
  const n = vals.length;
  const dp = new Array(n).fill(0);

  function findPrev(i) {
    let lo = 0, hi = i - 1, target = vals[i] - 3, ans = -1;
    while (lo <= hi) {
      const mid = (lo + hi) >> 1;
      if (vals[mid] <= target) { ans = mid; lo = mid + 1; }
      else { hi = mid - 1; }
    }
    return ans;
  }

  for (let i = 0; i < n; i++) {
    const prev = findPrev(i);
    const take = sums[i] + (prev >= 0 ? dp[prev] : 0);
    const skip = i > 0 ? dp[i - 1] : 0;
    dp[i] = Math.max(skip, take);
  }

  return dp[n - 1] || 0;
};

πŸ§ͺ Edge Cases

  • All same values β†’ take them all.

  • Values spaced far apart β†’ no conflict, take everything.

  • Consecutive values β†’ need to skip wisely.


πŸŽ₯ Reflections

This problem is fun because it mixes greedy grouping with DP thinking.

  • Grouping turns the input into smaller chunks.

  • DP with binary search makes it efficient.

That’s it for Day 26 of my LeetCode journey!
On to the next challenge πŸ”₯

Happy Coding πŸ‘¨β€πŸ’»