1、458. 可怜的小猪
有 1000 只水桶,其中有且只有一桶装的含有毒药,其余装的都是水。它们从外观看起来都一样。如果小猪喝了毒药,它会在 15 分钟内死去。
问题来了,如果需要你在一小时内,弄清楚哪只水桶含有毒药,你最少需要多少只猪?
回答这个问题,并为下列的进阶问题编写一个通用算法。
进阶:
假设有 n 只水桶,猪饮水中毒后会在 m 分钟内死亡,你需要多少猪(x)就能在 p 分钟内找出 “有毒” 水桶?这 n 只水桶里有且仅有一只有毒的桶。
提示:
- 可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
- 小猪喝完水后,必须有 m 分钟的冷却时间。在这段时间里,只允许观察,而不允许继续饮水。
- 任何给定的桶都可以无限次采样(无限数量的猪)。
解释:
这里说明一下如何使用两头猪检测25桶水。
第一步:对这25瓶液体使用5进制编码,如下。你也可以看成一个[x,y]的二维坐标。
pig1
40 41 42 43 44
30 31 32 33 34
20 21 22 23 24 pig2
10 11 12 13 14
00 01 02 03 04
第二步:开始喝液体(两只猪分别按行、列开喝)
第一只猪,在第0min钟,把第一列[40 30 20 10 00]这5桶水全喝了。结果被毒死了(<15min见效)。第一只猪不能再喝了。
第二只猪,在第0min钟,把第一行[00 01 02 03 04]这5桶水全喝了,什么事都没有…
第15min钟,继续喝第二行[10 11 12 13 14]…
第30min钟,继续喝第三行[20 21 22 23 24]这五桶水全喝了,结果被毒死了。
第三步:那么,有毒的液体就是编码为20的那瓶。
总结,一只猪每15min钟都有一个死活状态。0min 15min 30min 45min 60min。而我们,通过他们的死亡时间,推断出有毒液体。
可怜的小猪。没有幸存者。
是的,最终,这两只猪都死了…
代码:
方法1:
//458. 可怜的小猪
public int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
int times = minutesToTest / minutesToDie + 1;
//Math.log(n):求以e为低,n的对数
//Math.log(buckets) / Math.log(times):求以times为低,buckets的对数
int count = (int) Math.ceil(Math.log(buckets) / Math.log(times));
return count;
}
方法2:
/*
times = test/die +1 ; 将木桶编号转化成times(5)进制的数, 每个小猪去确定一位数,规定时间内可以喝 test/die(4) 次, 如果最终没死,说明该位取该进制下最大的数(4); 形象化描述的话,就是建立具有小猪个数(x)维度的坐标系.
*/
public int poorPigs2(int buckets, int minutesToDie, int minutesToTest) {
int times = minutesToTest / minutesToDie + 1;
int i = 0;
int number = 1;
while (number < buckets) {
number *= times;
i++;
}
return i;
}