Description
You are given an integer array power where power[i] is the power of the ith monster.
You start with 0 mana points, and each day you increase your mana points by gain where gain initially is equal to 1.
Each day, after gaining gain mana, you can defeat a monster if your mana points are greater than or equal to the power of that monster. When you defeat a monster:
- your mana points will be reset to
0, and - the value of
gainincreases by1.
Return the minimum number of days needed to defeat all the monsters.
Example 1:
Input: power = [3,1,4] Output: 4 Explanation: The optimal way to beat all the monsters is to: - Day 1: Gain 1 mana point to get a total of 1 mana point. Spend all mana points to kill the 2nd monster. - Day 2: Gain 2 mana points to get a total of 2 mana points. - Day 3: Gain 2 mana points to get a total of 4 mana points. Spend all mana points to kill the 3rd monster. - Day 4: Gain 3 mana points to get a total of 3 mana points. Spend all mana points to kill the 1st monster. It can be proven that 4 is the minimum number of days needed.
Example 2:
Input: power = [1,1,4] Output: 4 Explanation: The optimal way to beat all the monsters is to: - Day 1: Gain 1 mana point to get a total of 1 mana point. Spend all mana points to kill the 1st monster. - Day 2: Gain 2 mana points to get a total of 2 mana points. Spend all mana points to kill the 2nd monster. - Day 3: Gain 3 mana points to get a total of 3 mana points. - Day 4: Gain 3 mana points to get a total of 6 mana points. Spend all mana points to kill the 3rd monster. It can be proven that 4 is the minimum number of days needed.
Example 3:
Input: power = [1,2,4,9] Output: 6 Explanation: The optimal way to beat all the monsters is to: - Day 1: Gain 1 mana point to get a total of 1 mana point. Spend all mana points to kill the 1st monster. - Day 2: Gain 2 mana points to get a total of 2 mana points. Spend all mana points to kill the 2nd monster. - Day 3: Gain 3 mana points to get a total of 3 mana points. - Day 4: Gain 3 mana points to get a total of 6 mana points. - Day 5: Gain 3 mana points to get a total of 9 mana points. Spend all mana points to kill the 4th monster. - Day 6: Gain 4 mana points to get a total of 4 mana points. Spend all mana points to kill the 3rd monster. It can be proven that 6 is the minimum number of days needed.
Constraints:
1 <= power.length <= 171 <= power[i] <= 109
Solutions
Solution 1: State Compression + Memoization Search
We note that the number of monsters is at most 17, which means we can use a 17-bit binary number to represent the state of the monsters. The i-th bit being 1 indicates that the i-th monster is still alive, and 0 indicates that the i-th monster has been defeated.
We design a function dfs(mask) to represent the minimum number of days needed to defeat all monsters when the current state of the monsters is mask. The answer is dfs(2n - 1), where n is the number of monsters.
The calculation of the function dfs(mask) is as follows:
- If mask = 0, it means all monsters have been defeated, return 0;
- Otherwise, we enumerate each monster i. If the i-th monster is still alive, we can choose to defeat the i-th monster, then recursively calculate dfs(mask \oplus 2i), and update the answer to ans = min(ans, dfs(mask \oplus 2i) + \lceil x⁄gain \rceil), where x is the strength of the i-th monster, and gain = 1 + (n - mask.bitcount()) represents the current daily mana gain.
Finally, we return dfs(2n - 1).
The time complexity is O(2n × n), and the space complexity is O(2n). Here, n is the number of monsters.
class Solution: def minimumTime(self, power: List[int]) -> int: @cache def dfs(mask: int) -> int: if mask == 0: return 0 ans = inf gain = 1 + (n - mask.bit_count()) for i, x in enumerate(power): if mask >> i & 1: ans = min(ans, dfs(mask ^ (1 << i)) + (x + gain - 1) // gain) return ans n = len(power) return dfs((1 << n) - 1)(code-box)
Solution 2: State Compression + Dynamic Programming
We can convert the memoization search in Solution 1 to dynamic programming. Define f[mask] to represent the minimum number of days needed to defeat all monsters when the current state of the monsters is mask. Here, mask is an n-bit binary number, where the i-th bit being 1 indicates that the i-th monster has been defeated, and 0 indicates that the i-th monster is still alive. Initially, f[0] = 0, and the rest f[mask] = +∞. The answer is f[2n - 1].
We enumerate mask in the range [1, 2n - 1]. For each mask, we enumerate each monster i. If the i-th monster is defeated, it can be transferred from the previous state mask \oplus 2i, with a transfer cost of (power[i] + gain - 1) / gain, where gain = mask.bitCount().
Finally, return f[2n - 1].
The time complexity is O(2n × n), and the space complexity is O(2n). Here, n is the number of monsters.
class Solution: def minimumTime(self, power: List[int]) -> int: n = len(power) f = [inf] * (1 << n) f[0] = 0 for mask in range(1, 1 << n): gain = mask.bit_count() for i, x in enumerate(power): if mask >> i & 1: f[mask] = min(f[mask], f[mask ^ (1 << i)] + (x + gain - 1) // gain) return f[-1](code-box)
