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));
};
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.
minutesToDie
minutes.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.
Let's break down the solution step by step:
minutesToDie
minutes after drinking poison, and you have minutesToTest
minutes, you can conduct minutesToTest // minutesToDie
full rounds of testing.
states = minutesToTest // minutesToDie + 1
.
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.
n
such that states^n >= buckets
. Taking logarithms, n = ceil(log(buckets) / log(states))
.
This approach is efficient and leverages information theory to minimize the number of pigs.
Suppose buckets = 1000
, minutesToDie = 15
, and minutesToTest = 60
.
60 / 15 = 4
rounds4 + 1 = 5
(dies in round 1, 2, 3, 4, or survives all)n
such that 5^n >= 1000
.5^4 = 625
(not enough), 5^5 = 3125
(enough), so n = 5
pigs.
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.