Leetcode Daily Challenge Week 2: November 8th - November 14th: Poor Pigs

一、概述

现在有1000杯水,其中有一杯有毒。你有若干只猪,猪喝完水会在15分钟内挂掉。一只猪一次可以喝许多杯水。你有60分钟,请问最少使用多少只猪能够确定那杯有毒的水?
扩展:
现在有s杯水,其中有一杯有毒。你有若干只猪,猪喝完水会在x分钟内挂掉。一只猪一次可以喝许多杯水。你有y分钟,请问最少使用多少只猪能够确定那杯有毒的水?

二、分析

好鸡儿难啊。这是最近总在群里或者论坛里看到的一道题。冠名为微软2012面试题等等。今天在LeetCode的每日一题看到了。本来以为简单,但是做了好几个小时才做出来。
我把我走过的坑一点一点说一下。
首先,咱们要试出两个样例:
第一个:有25杯水,最多做4次试验时,猪有2只;有26杯水,最多做4次试验时,猪有3只。
第二个:有125杯水,最多做4次试验时,猪有3只;有126杯水,最多做4次试验时,猪有4只。
先看第一个样例。
第一个样例告诉我们,猪有2只且最多做4次试验时,最多辨认出25杯水。
在这里我就卡了快一个小时。啥子?俩猪能辨别出25杯水?咋搞的?
这样搞的:
第一次试验:
将1,2,3,4,5喂给一号猪。
将6,7,8,9,5喂给二号猪。
若一号猪死球了,那么说明毒水在1,2,3,4中。
若二号猪死球了,那么说明毒水在6,7,8,9中。
以上两种情况,还有三次试验,还剩一只猪,一瓶一瓶喂,就能确定四杯水哪杯有毒。
若俩猪都死球了,那么说明毒水是5号。
若没有猪死,那么排除了9杯水。
第二次试验:
将10,11,12,13喂给一号猪。
将14,15,16,13喂给二号猪。
若一号猪死球了,那么说明毒水在10,11,12中。
若二号猪死球了,那么说明毒水在14,15,16中。
以上两种情况,还有两次试验,还剩一只猪,一瓶一瓶喂,就能确定三杯水哪杯有毒。
若俩猪都死球了,那么说明毒水是13号。
若没有猪死,那么排除了7杯水。
第三次试验同理。可以排除5杯水。
最后一次试验,由于必定剩两只活猪,那么可以排除2^2=4杯水。
所以9+7+5+4=25杯。
主要不容易想到的就是猪很宝贵,能不死就不死。不要用二分什么的。
另外如何确定第一次试验要喂给每只猪几杯水呢?
喂5杯。这个5是怎么来的?4+1。其中,4是有一只猪死了,剩下试验能辨别出的最大杯数(剩一只猪,剩三次试验),1是根据猪死了的状态直接判断哪杯是毒水。好。这个是核心。下面拓展到第二个样例。
第二个样例告诉我们,猪有三只,且最多做四次试验时,最多辨认125杯水。哦豁。效率提高了这么多。咋办到的?这样搞:
第一次试验:
将1,2,3,···,14,15,16,49,50,51,52,57,58,59,60,61喂给一号猪。
将17,18,19,···,30,31,32,49,50,51,52,53,54,55,56,61喂给二号猪。
将33,34,35,···,46,47,48,53,54,55,56,57,58,59,60,61喂给三号猪。
若一号猪死球了,那么说明毒水在1,2,3,···,14,15,16中。
若二号猪死球了,那么说明毒水在17,18,19,···,30,31,32中。
若三号猪死球了,那么说明毒水在33,34,35,···,46,47,48中。
以上三种情况,还有三次试验,还有两只猪,我们来看第一个样例,两只猪三次试验,最多辨认7+5+4=16杯水。所以给每只猪单独喝的是16杯水。
若一号猪和二号猪死球了,那么说明毒水在49,50,51,52中。
若二号猪和三号猪死球了,那么说明毒水在53,54,55,56中。
若一号猪和三号猪死球了,那么说明毒水在57,58,59,60中。
以上三种情况,还有三次试验,还有一只猪。一杯一杯喂,可以辨认4杯水。所以两只猪喝同样的水的是4杯。
若三只猪都死球了,61号是毒水。
若没有猪死球,排除61杯水
从以上可以看出,第一次排除的61,是48+12+1得到的。
其中48,是16*3得到的。其中16,是上一个样例的7+5+4得到的。
其中12比较难以得到,这个是最难想的。大家一般都会想到这种形式:
即把49喂给一号和二号猪,50喂给二号三号,51喂给一号三号。看猪死的状态直接确定哪杯水是毒水。但实际上,这浪费了剩余一只猪的鉴定价值。按上面这种形式,如果50号是毒水,那么直接确定。但如果我们把50和51都喂给一号二号呢?那么当一号二号死球了,我们可以确定50或51有毒,之后用三号就能确定哪杯有毒了。这个是十分重要的。
那么12如何计算出来呢?用组合数。在三只猪中选二只死球,一只活着,共有三种选法。剩下一只猪,三次试验。可以确定4杯水。因此三种选法乘以四杯水就得到了12。
这样我们来看,本题有两个关键变量:
其一,每次试验能够排除的水的最大杯数,记为f;
其二,所有试验做完了能够排除水的最大杯数,记为F。
设f(n,p)为还有n次试验,还有p只猪,本次实验能够排除的水的杯数。例如,f(4,2)=9,f(4,3)=61。
注意,f(1,p)=2^p,f(n,1)=1,f(1,1)=2。
设F(n,p)为还有n次试验,还有p只猪,所有试验做完了最多能够排除水的杯数。例如,F(4,2)=25,F(4,3)=125。
那么我们可以归纳出F和f的计算公式如下:
f ( n , p ) = 1 + ∑ i = 1 p − 1 C p i F ( n − 1 , p − i ) F ( n , p ) = ∑ i = 1 n f ( i , p ) f(n,p)=1+\sum_{i=1}^{p-1}C_{p}^{i}F(n-1,p-i)\\ F(n,p)=\sum_{i=1}^{n}f(i,p) f(n,p)=1+i=1p1CpiF(n1,pi)F(n,p)=i=1nf(i,p)

三、总结

好麻烦啊!好复杂啊!那个2只猪试验25杯水我自己想的怀疑人生了= =
PS:
代码如下:

class Solution {
    int C(int a,int b)
    {
        long long m1=1,c=b+1;
        while(c<=a)
        {
            m1*=c;
            ++c;
        }
        int m2=1,d=1;
        while(d<=(a-b))
        {
            m1/=d;
            d++;
        }
        return m1;
    }
    int f(int n,int p)
    {
        if(n==1)
            return pow(2,p);
        if(p==1)
            return 1;
        int res=0,pp=1;
        while(pp<=p-1)
        {
            res+=C(p,pp)*F(n-1,p-pp);
            ++pp;
        }
        return res+1;
    }
    int F(int n,int p)
    {
        int res=0;
        for(int i=n;i>0;--i)
        {
            int t=f(i,p);
            res+=t;
        }
        return res;
    }
public:
    int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        if(buckets==1)
            return 0;
        int n=minutesToTest/minutesToDie;
        int res=1;
        int all=F(n,1);
        while(all<buckets)
        {
            ++res;
            all=F(n,res);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值