Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

458. Poor Pigs - Leetcode Solution

Code Implementation

import math
class Solution:
    def poorPigs(self, buckets: int, minutesToDie: int, minutesToTest: int) -> int:
        # Number of states a pig can show: die in 1st, 2nd, ..., or survive all rounds
        states = minutesToTest // minutesToDie + 1
        # We need the smallest n such that states^n >= buckets
        return math.ceil(math.log(buckets) / math.log(states))
      
#include <cmath>
class Solution {
public:
    int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        int states = minutesToTest / minutesToDie + 1;
        return ceil(log(buckets) / log(states));
    }
};
      
class Solution {
    public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        int states = minutesToTest / minutesToDie + 1;
        return (int)Math.ceil(Math.log(buckets) / Math.log(states));
    }
}
      
var poorPigs = function(buckets, minutesToDie, minutesToTest) {
    let states = Math.floor(minutesToTest / minutesToDie) + 1;
    return Math.ceil(Math.log(buckets) / Math.log(states));
};
      

Problem Description

You are given buckets buckets, one of which contains a deadly poison. The rest are filled with water. You have a number of pigs that you can use for testing. The poison will kill a pig minutesToDie minutes after it drinks the poison. You have a total of minutesToTest minutes to determine which bucket contains the poison.

Each pig can drink from any number of buckets, and you can observe whether a pig dies or survives after each round. The goal is to determine the minimum number of pigs needed to guarantee finding the poisoned bucket within the allotted time.

  • Each pig can be used multiple times (as long as it survives).
  • Assume you can split the tests into rounds, each round lasting minutesToDie minutes.
  • You must guarantee finding the poisoned bucket in any scenario.

Thought Process

At first glance, it might seem like you need to try every possible combination by feeding pigs from different buckets, but this quickly becomes impractical with larger numbers of buckets. Instead, we need to maximize the information each pig can provide.

Each pig can be used in multiple rounds, and its survival or death at each round can encode information. By cleverly distributing which buckets each pig drinks from and observing the timing of their deaths, we can uniquely identify the poisoned bucket.

The key insight is to think of each pig as representing a "digit" in a number system, where the base is equal to the number of distinct outcomes (states) a pig can have during the test period.

Solution Approach

Let's break down the solution step by step:

  1. Determine the number of rounds: Since a pig dies minutesToDie minutes after drinking poison, and you have minutesToTest minutes, you can conduct minutesToTest // minutesToDie full rounds of testing.
  2. States per pig: Each pig can die in any one of the rounds, or survive all rounds. So, the number of states per pig is states = minutesToTest // minutesToDie + 1.
  3. Encoding information: If you have n pigs, each with states possible outcomes, together they can encode states^n unique results. This means you can test up to states^n buckets.
  4. Finding the minimum pigs: We need the smallest n such that states^n >= buckets. Taking logarithms, n = ceil(log(buckets) / log(states)).
  5. Implementation: Use logarithms and ceiling to compute the minimum pigs needed.

This approach is efficient and leverages information theory to minimize the number of pigs.

Example Walkthrough

Suppose buckets = 1000, minutesToDie = 15, and minutesToTest = 60.

  • Number of rounds: 60 / 15 = 4 rounds
  • States per pig: 4 + 1 = 5 (dies in round 1, 2, 3, 4, or survives all)
  • How many pigs needed? Find smallest n such that 5^n >= 1000.
  • 5^4 = 625 (not enough), 5^5 = 3125 (enough), so n = 5 pigs.
  • Each pig represents a digit in a 5-base number. Assign each bucket a unique 5-digit number. For each round, make pigs drink from buckets according to their digit in that round. After the tests, the pattern of deaths tells you exactly which bucket is poisoned.

Time and Space Complexity

  • Brute-force approach: If you tried every possible combination, time and space would be exponential in the number of buckets and pigs, which is impractical.
  • Optimized approach: The solution only involves some basic arithmetic and logarithms, so it runs in O(1) time and uses O(1) space.
  • Why O(1)? All calculations are done in constant time, regardless of the input size.

Summary

The "Poor Pigs" problem is a classic example of maximizing information gain per test. By viewing each pig as encoding information through its possible death/survival outcomes, and mapping this to a base-N number system, we can efficiently determine the minimum number of pigs needed. The key insight is using logarithms to relate the number of buckets to the number of pigs and rounds, making the solution both elegant and optimal.