Taro Logo

Minimum Amount of Damage Dealt to Bob

Hard
Asked by:
Profile picture
12 views
Topics:
ArraysGreedy Algorithms

You are given an integer power and two integer arrays damage and health, both having length n.

Bob has n enemies, where enemy i will deal Bob damage[i] points of damage per second while they are alive (i.e. health[i] > 0).

Every second, after the enemies deal damage to Bob, he chooses one of the enemies that is still alive and deals power points of damage to them.

Determine the minimum total amount of damage points that will be dealt to Bob before all n enemies are dead.

Example 1:

Input: power = 4, damage = [1,2,3,4], health = [4,5,6,8]

Output: 39

Explanation:

  • Attack enemy 3 in the first two seconds, after which enemy 3 will go down, the number of damage points dealt to Bob is 10 + 10 = 20 points.
  • Attack enemy 2 in the next two seconds, after which enemy 2 will go down, the number of damage points dealt to Bob is 6 + 6 = 12 points.
  • Attack enemy 0 in the next second, after which enemy 0 will go down, the number of damage points dealt to Bob is 3 points.
  • Attack enemy 1 in the next two seconds, after which enemy 1 will go down, the number of damage points dealt to Bob is 2 + 2 = 4 points.

Example 2:

Input: power = 1, damage = [1,1,1,1], health = [1,2,3,4]

Output: 20

Explanation:

  • Attack enemy 0 in the first second, after which enemy 0 will go down, the number of damage points dealt to Bob is 4 points.
  • Attack enemy 1 in the next two seconds, after which enemy 1 will go down, the number of damage points dealt to Bob is 3 + 3 = 6 points.
  • Attack enemy 2 in the next three seconds, after which enemy 2 will go down, the number of damage points dealt to Bob is 2 + 2 + 2 = 6 points.
  • Attack enemy 3 in the next four seconds, after which enemy 3 will go down, the number of damage points dealt to Bob is 1 + 1 + 1 + 1 = 4 points.

Example 3:

Input: power = 8, damage = [40], health = [59]

Output: 320

Constraints:

  • 1 <= power <= 104
  • 1 <= n == damage.length == health.length <= 105
  • 1 <= damage[i], health[i] <= 104

Solution


Clarifying Questions

When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:

  1. What are the possible ranges for the damage values, and can damage values be negative or zero?
  2. Is there a minimum amount of damage Bob must receive, or can Bob take zero damage?
  3. If it's impossible to deal enough damage to Bob, what should the function return?
  4. Are there any restrictions on the number of attacks I can use?
  5. Can the input contain duplicate damage values, and if so, should I consider them separately?

Brute Force Solution

Approach

The goal is to figure out the smallest amount of damage Bob can take by strategically using shields to block attacks. The brute force approach involves trying every possible combination of shield placements against the sequence of attacks.

Here's how the algorithm would work step-by-step:

  1. Consider every possible combination of choosing which attacks to shield against.
  2. For each of these combinations, calculate the total damage Bob would take, considering the attacks blocked by shields.
  3. Keep track of the minimum total damage across all these combinations.
  4. After checking every single combination of shields, the minimum total damage recorded is the answer.

Code Implementation

def minimum_damage_brute_force(attacks, shield_strength):
    number_of_attacks = len(attacks)
    minimum_total_damage = float('inf')

    # Iterate through all possible combinations of shield usage.
    for i in range(2 ** number_of_attacks):
        total_damage_taken = 0
        
        # Check each attack to see if the shield is used.
        for attack_index in range(number_of_attacks):
            # Calculate the damage taken for the current shield combination.
            if (i >> attack_index) & 1:

                #Shield is used. Reduce damage or make it zero
                damage_taken = max(0, attacks[attack_index] - shield_strength)
                total_damage_taken += damage_taken

            else:
                #No shield used
                total_damage_taken += attacks[attack_index]

        # Update the minimum damage if the current combination is lower.
        minimum_total_damage = min(minimum_total_damage, total_damage_taken)

    return minimum_total_damage

Big(O) Analysis

Time Complexity
O(2^n)The algorithm iterates through every possible combination of shielding attacks, where n is the number of attacks. This means for each attack, we either shield it or we don't, resulting in 2 possibilities. Since there are n attacks, there are 2*2*...*2 (n times) or 2^n possible combinations. Therefore, the time complexity is exponential with respect to the number of attacks.
Space Complexity
O(1)The provided brute force approach iterates through every possible combination of shield placements. Although the number of combinations is exponential, the described implementation only needs to store the minimum damage calculated so far and potentially a few variables to track the current combination being evaluated. These variables require a constant amount of space, independent of the number of attacks (N). No auxiliary data structures like arrays or hash maps are used to store intermediate results or combinations. Therefore, the auxiliary space complexity is O(1).

Optimal Solution

Approach

The goal is to find the smallest damage amount when hitting Bob, which we achieve by strategically selecting attacks. We will evaluate the attacks and choose the combination that results in the absolute least amount of damage.

Here's how the algorithm would work step-by-step:

  1. First, sort the attack values from lowest to highest.
  2. Select the attack with the lowest damage value.
  3. Consider the number of times each type of attack can be used. For example, if you can use the lowest attack multiple times, then use it multiple times.
  4. If using the lowest damage type is not enough to hit Bob, include the attack with the next lowest damage value.
  5. Continue to add attacks with the lowest damage values until the total damage is enough to hit Bob.
  6. Calculate the sum of all the attacks that were selected.
  7. The sum is the minimum possible damage that can be dealt to Bob.

Code Implementation

def minimum_damage_dealt(attack_damages, bob_health):
    attack_damages.sort()
    total_damage = 0
    minimum_damage = 0

    # Iterate through sorted attacks to find minimum damage
    for attack_damage in attack_damages:
        while total_damage < bob_health:

            # Add the lowest damage until Bob is hit
            total_damage += attack_damage
            minimum_damage += attack_damage

            if total_damage >= bob_health:
                return minimum_damage

    # Handle cases where Bob cannot be hit
    return minimum_damage

Big(O) Analysis

Time Complexity
O(n log n)The initial step involves sorting the attack values, which takes O(n log n) time using an efficient sorting algorithm like merge sort or quicksort. The subsequent steps involve iterating through the sorted array and adding attacks until the total damage is sufficient. This iteration takes at most O(n) time. Therefore, the dominant operation is the sorting step, making the overall time complexity O(n log n).
Space Complexity
O(1)The provided algorithm sorts the input array in-place, implying no additional array is created for sorting, thus contributing O(1) space. The algorithm then iterates through the sorted array, summing the selected attack values. This summation process only involves a few constant space variables for storing the current damage total and loop indices. Therefore, the algorithm uses constant auxiliary space, independent of the input size N (number of attacks). Hence the overall space complexity is O(1).

Edge Cases

Null or empty input array
How to Handle:
Return 0 immediately as no damage can be dealt.
Array with a single element
How to Handle:
Return 0, since dealing damage requires at least two actions.
Input array contains only zeros
How to Handle:
Return 0, as no damage can be dealt using only zeros.
Input array contains very large numbers leading to potential integer overflow during damage calculation.
How to Handle:
Use appropriate data types (e.g., long in Java/C++) to prevent integer overflow during the damage calculation step.
No possible damage dealing combination exists (e.g., all actions have negative impact)
How to Handle:
Return 0, since our objective is to minimize the damage dealt and not deal any at all, if possible.
Input array with a very large number of elements, potentially exceeding memory limits.
How to Handle:
Consider algorithmic approaches with lower space complexity, or streaming data if applicable to reduce memory footprint
Input array contains negative values
How to Handle:
Account for negative values in the damage calculation, as some action may require negative damage, which could minimize damage dealt
Actions with extreme values (both very large positive and very large negative)
How to Handle:
Handle edge cases and prioritize extremely negative action values to reduce damage dealt as much as possible.