那么这里的x和n会是多少呢?最后一个区块里包含1个楼层,倒数第二个包含2个楼层,倒数第三个包含3个楼层,继续,各区块包含的楼层是4,5,6,……
于是问题转变为,到包含多少个楼层的时候,100个楼层全部分配完?由于1+2+3+……+13=91,而1+2+3+……+14=105,就是说从1开始累加,加到14时,总和第一次大于100:所以上图里的x是14。
问题解决:第一个球依次试14,27,39,50,60,69,77,84,90,95,99,100。中间任何一次破碎了,就从上一次的下一层开始用第二个球逐层尝试,直至第二个球也破碎为止。用这个方法,总次数一定不超过14次:当最高安全楼层越来越高时,第一个球试的次数越来越多,但第二个球试的次数越来越少,两者始终维持着一种平衡。
该思路简单易懂,但当球的的个数(即i的值)增加的时候,分块的会变得越来越难。
本思路参考:
(二)动态规划思想
下面提供两个方案:
方案一:
一.方案概述
dp(i,j):表示用i个球测试j层楼一定可测出某一层是最高安全层的最少次数,等同于min{ max{ 1+dp(i-1,k-1),1+dp(i,j-k) },0<=k<=j} 。
现在给定i个球,j层楼,假设我们在某一层k扔一个球,那么就会出现两种情况:
(1)如果球碎了,那我们需要往第k层楼以下的楼层去测试。在第k层我们测试了,所以次数要计数1;还需往下的楼层作测试,这时还有i-1个球去测试k-1层楼,即需计次数 dp[i-1][k-1]。综上,在情况(1)下,测试次数为1+dp(i-1,k-1)。
(2)如果球不碎,那我们需要往第k层楼以上的楼层去测试。在第k层我们测试了,所以次数要计数1;还需往上的楼层作测试,这时还有i个球去测试j-k层楼,即需计次数 dp[i][j-k]。综上,在情况(2)下,测试次数为1+dp(i,j-k)。
在(1)(2)两种情况中,我们需要去两者的最大值,因为我们需要保证一定可以测试出最高安全楼层。由上可见,k是一个变量,取值范围为0<=k<=j。这表明我们需要遍历0j,每次求出12两种情况的最大值,为了简便,我们将0j遍历所得的最大值记为{m0,m1,m2…mj},我们还需要在这些值(这些值都是可以保证一定能测试出最高安全楼层)中选出最小值,因为我们需要的是最少的次数。综上,dp(i,j)= min{ max{ 1+dp(i-1,k-1),1+dp(i,j-k) },0<=k<=j}。
这个方案是简单易懂的,但有一定的问题,就是在j足够大的时候。假设给出的j是10^15。那么按照这个方案,求dp(i,j)我需要去循环10^15次(这是不计比较的,若计比较的次数,在求Max的时候就需要比较10^15次,再而求min,又需要10^15-1次比较),这只是求出某一个dp[i][j]的循环次数,若要生成表的话,那循环的次数就是元素个数*10^15次,这将是巨大的了,这是关于该方案时间复杂度的问题,而在该方案的数据存储效果也没有一定的优点,这里还是以j为10^15为假设,那么就需要开一个数组为Array[球的个数][10^15]这样的大小了。所以在此仅提供思考方向,供读者参考。
方案二:
一.方案概述
dp(i,j):表示min{ k | 用i个球可尝试的次数为j次一定可测出k层楼的某一层是最高安全层}
现在给定i个球,j次测试次数,假设我们在某一层t扔一个球,那么就会出现两种情况:
(1)所扔球的球碎了,那么t层以上的楼层所扔的球一定会碎,这时我们就需要用剩下的i个球和j-1次测试次数往下去测,即dp(i-1,j-1),那么这时的k= 1+dp(i-1,j-1) +∞=∞(∞表示无穷大)。在这里为k的值作下解释:我们在t层扔球了一个球,t层已经测试了,所以式子出现“+1”,接着t层扔的球碎了,那我们需要往下继续测试,这就对应了式子中的“+dp(i-1,j-1)”,而式子中的“+∞”是t层以上的楼我们是可以确定测试结果的,就相当于我们测试了。综上,在情况1下,k=∞。
(2)所扔球的球不碎,那么这时我们就需要用剩下的i个球和j-1次测试次数往上去测,即dp(i,j-1),而对于t层以下的楼层呢?那所扔的球一定不会碎,那么这时的k= dp(i-1,j-1)+1+dp(i,j-1)。在这里也为k的值作下解释:我们在t层扔球了一个球,t层已经测试了,所以式子依然要“+1”,接着t层扔的球不碎,那我们需要往上继续测试,这就对应了式子中的“+dp(i,j-1)”,而对于t层以下的楼层是可以确定测试结果的,就相当于我们测试了,即“+ dp(i-1,j-1)”(注意t层以下的楼层是需要被统计的,这点是难点,也容易被遗忘)。综上,在情况2下,k= dp(i-1,j-1)+1+dp(i,j-1)。
综合上面两种情况,dp(i,j)=min{k | 用i个球可尝试的次数为j次一定可测出k层楼的某一层是最高安全层}=min{∞,dp(i-1,j-1)+1+dp(i,j-1)}= dp(i-1,j-1)+1+dp(i,j-1)。
二.实例演示
接下来我们用第一个例子(即i=3,N=7)来进行方案一的推导。为了方便读者,下面提供前面的图解:
我们用上面方案一的状态方程求出来的解(用二维数组存)如下图:
下面是dp(3,3)=7的推导过程。我们先给出该推导过程的图解,读者可以结合该图来理解下面的文字说明。
(1)按照状态方程,dp(3,3)=dp(2,2)+1+dp(3,2),说明我们有3个球,可用的测试次数为3,我们在某一层扔一个球,其实扔球的这一层是第4层,为什么呢?我们在某一层扔球,该层已经测试过了,所以计数为1,接着这一层往下的楼层我们已知测试结果即球不会碎,则我们需要将下面的楼层也计数,即dp(2,2)+1,查上方的数组,我们得到结果dp(2,2)+1=4,那么换句话说,我们第一次扔球的层数就是在第4层。
(2)如果在第4层扔球,球碎了,那往下去测试,即dp(2,2);按照状态方程,dp(2,2)=dp(1,1)+1+dp(2,1),同理我们第二次扔球的楼层是第dp(1,1)+1=2层。在第2层扔球又出现两种情况:a.球碎了,那继续往下去测试,即dp(1,1)=dp(0,0)+1+dp(1,0),第三次扔球的楼层是第1+dp(0,0)=1层;b.球不碎,往上去测试,即dp(2,1)=dp(1,0)+1+dp(2,0),那第三次扔球的楼层就是第2+1+dp(1,0)=3层(由于是往上去测试,所以要加上上一次扔球的层数,在这里是上一次扔球的层数是第2层)
(3)如果在第4层扔球,球不碎,那就往上去测试,即dp(3,2);按照状态方程,dp(3,2)=dp(2,1)+1+dp(3,1),同理我们第二次扔球的楼层是第4+dp(2,1)+1=6层。在第6层扔球又有两种情况:a.球碎了,那继续往下去测试,即dp(2,1)=dp(1,0)+1+dp(2,0),第三次扔球的楼层是第4+1+dp(1,0)=5层;b.球不碎,往上去测试,即dp(3,1)=dp(2,0)+1+dp(3,0),那第三次扔球的楼层就是第6+1+dp(2,0)=7层
由上图可见,扔球的结果成一个树状图,每次我们只能走其中的一条路径,那么一定能准确测出最高安全楼层的最少的扔球次数为3。
三.伪代码
根据方案二的原理,其方程如下:
伪代码如下:
for i to 球的个数
forj to Maxj(由于j是我们所要求的,则可依题给定j的最大限度Maxj)
if(i==0||j==0)//无球或无测试次数
dp[i][j]= 0
else
/***在此可以添加条件来输出需要的值***/
dp[i][j] = dp[i][j - 1] + 1 + dp[i - 1][j - 1]
可见该方案的时间复杂度为O(球的个数*Maxj)。
本人是一个喜欢算法的新手,本博客简要的阐明了对一道DP问题(面试题)的解决思路,若本博客有错误需要修改的或对排版风格有要改进的等等的建议,请留言或发邮件给我。写博客是一个互相学习的过程,期待收到您的建议!记住,不要沮丧,好好学习,天天向上!共勉!
收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!