【算法】扔玻璃珠

题目
某幢大楼有100层。你手里有两颗一模一样的玻璃珠。当你拿着玻璃珠在某一层往下扔的时候,一定会有两个结果,玻璃珠碎了或者没碎。这幢大楼有个临界楼层。低于它的楼层,往下扔玻璃珠,玻璃珠不会碎,等于或高于它的楼层,扔下玻璃珠,玻璃珠一定会碎。玻璃珠碎了就不能再扔。现在让你设计一种方式,使得在该方式下,最坏的情况扔的次数比其他任何方式最坏的次数都少。也就是设计一种最有效的方式。

分析
1)最笨的办法:从第一层开始试,每次增加一层,当哪一层扔下玻璃珠后碎掉了,也就知道了。不过最坏的情况扔的次数可能为100。 
2)随便挑一层,假如为N层,扔下去后,如果碎了,那就只能从第一层开始试了,最坏的情况可能为N;假如没碎,就一次增加一层继续扔吧,这时最坏的情况为100-N。也就是说,采用这种办法,最坏的情况为max{N, 100-N+1}。之所以要加一,是因为第一次是从第N层开始扔。 
3)假如没摔的话,不如不要一次增加一层继续扔吧,而是采取另外一种方式:把问题转换为100-N,在这里面找临界楼层,这样不就把问题转换成用递归的方式来解决。

定义一个函数F(N),表示N层楼最有效方式最坏情况的次数; 
通过上面的分析,有 
F(N)=Min( 
Max(1,1+F(N-1)), 
Max(2,1+F(N-2)) 
… 
Max(N-1,1+F(1)) 
); 
F(1)=1; 
求F(100);
 

int floor1(int N){
           int F[N+1];

           F[0]=F[1]=1;
           for(int i=2;i<=N;i++){
               F[i]=i;
               for(int j=1;j<i;j++){
                   int tmp=max(j, F[i-j]+1);
                   if(tmp<F[i]){
                       F[i]=tmp;
                   }
               }
           }
           return F[N];
       }
  int main()
      {
        int res=floor1(100);
          cout<<res;
          system("pause");
          return 0;
       }

将此题扩展为N个球,M层:

我们可以把M层楼 / N个鸡蛋的问题转化成一个函数 F(M,N),其中楼层数M和鸡蛋数N是函数的两个参数,而函数的值则是最优解的最大尝试次数。

假设我们第一个鸡蛋扔出的位置在第X层(1<=X<=M),会出现两种情况:

1.第一个鸡蛋没碎

那么剩余的M-X层楼,剩余N个鸡蛋,可以转变为下面的函数:

 F(M-X,N)+ 1,1<=X<=M

2.第一个鸡蛋碎了

那么只剩下从1层到X-1层楼需要尝试,剩余的鸡蛋数量是N-1,可以转变为下面的函数:

F(X-1,N-1) + 1,1<=X<=M

整体而言,我们要求出的是 M层楼 / N个鸡蛋 条件下,最大尝试次数最小的解,所以这个题目的状态转移方程式如下:

 F(M,N)= Min(Max( F(M-X,N)+ 1, F(X-1,N-1) + 1)),1<=X<=M

int getMinSteps(int eggNum, int floorNum){

   if(eggNum < 1 || floorNum < 1) {

       return 0;

   }

   //上一层备忘录,存储鸡蛋数量-1的floorNum层楼条件下的最优化尝试次数

   int preCache[floorNum+1];

   //当前备忘录,存储当前鸡蛋数量的floorNum层楼条件下的最优化尝试次数

   int currentCache[floorNum+1];


   //把备忘录每个元素初始化成最大的尝试次数

   for(int i=1;i<=floorNum; i++){

       currentCache[i] = i;

   }


   for(int n=2; n<=eggNum; n++){

       //当前备忘录拷贝给上一次备忘录,并重新初始化当前备忘录 

       for(int i=1;i<=floorNum; i++){
            preCache[i] = currentCache[i];
           currentCache[i] = i;

       }

       for(int m=1; m<=floorNum; m++){

           for(int k=1; k<m; k++){

               //扔鸡蛋的楼层从1到m枚举一遍,如果当前算出的尝试次数小于上一次算出的尝试次数,则取代上一次的尝试次数。

               //这里可以打印k的值,从而知道第一个鸡蛋是从第几次扔的。

               currentCache[m] = min(currentCache[m], 1+max(preCache[k-1],currentCache[m-k]));

           }

       }

   }

   return currentCache[floorNum];

}



int main() {

   cout<<getMinSteps(2,2080)<<endl;
   system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_41469381/article/details/89066416