Taro Logo

Poor Pigs

Hard
Amazon logo
Amazon
5 views
Topics:
Bit Manipulation

There are buckets buckets of liquid, where exactly one of the buckets is poisonous. You have minutesToTest minutes to determine which bucket is poisonous by feeding some number of pigs the liquid. A pig dies after minutesToDie minutes of drinking the poisonous liquid. A pig can be fed from any number of buckets, and each bucket can be fed from by any number of pigs. You can feed multiple pigs at the same time, it takes no time to feed a pig. Return the minimum number of pigs needed to figure out which bucket is poisonous within the allotted time.

For example:

Input: buckets = 4, minutesToDie = 15, minutesToTest = 15 Output: 2 Explanation: At time 0, feed the first pig buckets 1 and 2, and feed the second pig buckets 2 and 3. At time 15, there are 4 possible outcomes:

  • If only the first pig dies, then bucket 1 must be poisonous.
  • If only the second pig dies, then bucket 3 must be poisonous.
  • If both pigs die, then bucket 2 must be poisonous.
  • If neither pig dies, then bucket 4 must be poisonous.

Input: buckets = 4, minutesToDie = 15, minutesToTest = 30 Output: 2 Explanation: At time 0, feed the first pig bucket 1, and feed the second pig bucket 2. At time 15, there are 2 possible outcomes:

  • If either pig dies, then the poisonous bucket is the one it was fed.
  • If neither pig dies, then feed the first pig bucket 3, and feed the second pig bucket 4. At time 30, one of the two pigs must die, and the poisonous bucket is the one it was fed.

Solution


Let's analyze the "Poor Pigs" problem. This problem can initially seem tricky, but it boils down to a base conversion concept. Here's a breakdown:

Naive Approach

A brute-force approach would involve trying out different numbers of pigs and simulating the feeding process to see if we can uniquely identify the poisonous bucket within the given time. This is highly inefficient and impractical for larger inputs.

Optimal Approach

The key insight is that each pig can be thought of as providing information in a certain "base". Specifically, consider the number of rounds we can perform tests. The number of rounds can be calculated as rounds = minutesToTest / minutesToDie. If a pig dies at a certain round, this gives us a unique piece of information.

  • If we have one round of testing, each pig can have rounds + 1 outcomes (survive all rounds, die in round 1, die in round 2, etc.).
  • We want the minimum number of pigs such that (rounds + 1) ^ numPigs >= buckets.

Therefore, we need to find the smallest integer numPigs such that (minutesToTest / minutesToDie + 1) ^ numPigs >= buckets.

This can be solved by taking the logarithm base (minutesToTest / minutesToDie + 1) of both sides of the equation and rounding up to the nearest integer.

Code Implementation

import math

def poorPigs(buckets: int, minutesToDie: int, minutesToTest: int) -> int:
    rounds = minutesToTest // minutesToDie
    if buckets == 1:
        return 0
    return math.ceil(math.log(buckets, rounds + 1))

Example:

Let's take buckets = 1000, minutesToDie = 15, and minutesToTest = 60.

  1. rounds = 60 // 15 = 4
  2. We need to find the smallest numPigs such that (4 + 1) ^ numPigs >= 1000 i.e. 5 ^ numPigs >= 1000
  3. 5^4 = 625 < 1000 and 5^5 = 3125 >= 1000. Thus, numPigs = 5

Big O Analysis:

  • Time Complexity: O(1). The solution involves a few arithmetic operations and log which are constant time.
  • Space Complexity: O(1). The solution uses a constant amount of extra space.

Edge Cases:

  • buckets = 1: In this case, no pigs are needed since there is only one bucket and it must be the poisonous one. Return 0.
  • minutesToDie > minutesToTest: In this case, it's impossible to determine the poisonous bucket. The problem statement doesn't define behavior here, but in a real interview, you'd clarify the expected behavior. Since problem constraints don't include this, we don't need to handle this case. However, in practice, we may need to add a check if minutesToDie > minutesToTest: return -1 or throw an exception.

Explanation

The solution computes the minimum number of pigs by calculating the number of rounds possible (minutesToTest // minutesToDie), adding 1 to the rounds (to account for the pig surviving), and then determining the base-rounds + 1 logarithm of the number of buckets. The ceiling of this logarithm is the minimum number of pigs needed.