leetcode 887 鸡蛋掉落

非常经典,综合的题目。这一道题想到二分查找是不困难的,但是要想把二分查找正确的应用起来则需要使用动态规划的方法。将动态规划函数设置为dp(k,n)。根据在某一点的扔鸡蛋结果,如果碎了,代表需要在当前楼层以下的楼中进行测试,并且可用的鸡蛋数量少了一个,所以递归的函数为dp(k-1,x-1),若是没碎,则代表所要找的楼层肯定在当前楼层之上,递归函数为dp(k,n-x)。接下来则是最为困难也关键的过程。我们需要寻找的是这两个函数中最大值的最小值,也就是分别考虑两种情况下的最坏情况,并且进行比较,其中较小的值才是我们想要的。而从两个函数的表达式可以看出,分别为增函数与减函数,而这两个函数较大值的最小值,就是这两个函数曲线的交点处取的。也就是说,要通过递归实现对这个点的不断逼近,由于这个最小值点不一定是整数,所以需要取到两边的两个整数进行比较。而这个逼近的过程就可以通过二分查找来实现。这么一想下来,逻辑还是较为清晰的。贴代码。

class Solution {
public:
    unordered_map<int,int> memory;
    int superEggDrop(int k, int n) 
    {
        return dp(k,n);
    }
    int dp(int k,int n)
    {
        if(memory.find(n*100 + k) == memory.end())
        {
            int res;
            if(n == 0)
            res = 0;
            else if(k == 1)
            res = n;
            else
            {
                int l = 1;
                int h = n;
                while(l+1<h)
                {
                    int mid = (l+h)/2;
                    int x0 = dp(k-1,mid-1);
                    int x1 = dp(k,n-mid);
                    if(x0<x1)
                    l = mid;
                    else if(x0>x1)
                    h = mid;
                    else
                    l = h = mid;
                }
                res = 1+ min(max(dp(k-1,l-1),dp(k,n-l)),max(dp(k-1,h-1),dp(k,h-1)));
            }
            memory[n*100 + k] = res;            
        }
        return memory[n*100 + k];
    }
};

想了一晚上,有了一点关于双蛋问题的思路。该问题最重要的点在于,其所求的最小值,代表着即便每一步都是碎,也能完成所有楼层的测试。从这一个思路出发就会简单很多。首先假设最少的次数为k,第一次扔鸡蛋的楼层为m,因为只有两个鸡蛋,所以在最坏的情况下,也就是第一次碎了,剩下的k-1次机会也一定能够将剩下的m-1个楼层测试完成。在这个思路上,因为最后只剩一个鸡蛋,所以最稳妥的做法是从第一层开始一楼一楼往上爬,而一楼一楼爬最高能爬k-1楼,因为最多也只剩k-1次机会,当然也可以从更低的楼开始爬,但是就浪费了两个蛋的潜力。也就是说,这里的k = m。接下来,考虑第一次没碎的情况,这种情况下,可以把k层楼往下的所有楼层当成平地,这样一来,又变成了两个鸡蛋,k-1次机会的问题,这里能爬最高k-1楼。以此类推,为了能完全发挥两个蛋的潜力,自然要让这里的k变成1,而在k变成1之后,所有爬楼的总和一定大于楼层的总和,如果取100,那k就等于14,这样就完成了双蛋问题。

想明白了,官方题解中的逆向的方法,为什么状态转移方程会那样写,因为如果在某一层碎了,代表这一层网上的所有楼层都变成了天空,而剩下的k-1个蛋和t-1次机会是留给往下的楼层的。没碎同理,代表该楼层往下的所有楼层都是平地,剩下的k个蛋和t-1次机会是留给网上的楼层的,完成。贴代码

 1 int calcF(int K, int T)
 2     {
 3         if (T == 1 || K == 1) return T + 1;
 4         return calcF(K - 1, T - 1) + calcF(K, T - 1);
 5     }
 6 
 7     int superEggDrop(int K, int N)
 8     {
 9         int T = 1;
10         while (calcF(K, T) < N + 1) T++;
11         return T;
12     }

又想到了一个点,在这一公式中,碎裂所递归的式子的结果一定等于当前楼层的高度,因为这是蓄谋已久的,而不碎往上,则是意外之喜,在往上的过程中,同样需要考虑下一次碎了怎么办,或者说从下一次开始一直碎,碎到全部都碎了,这是最坏的情况,然而即便是最坏的情况也能够将该楼层往下的所有楼层全部查清。也就是说,当鸡蛋来到某一层,就代表着这一层往下的已经有十足的把握能够查清,如果运气好没碎,那就再按照规律往上爬,calcf(k-1,t-1)这个式子是来到这一层就已经获得了的,calcf(k-1,t-1)则是往上爬按照最稳妥的规律能够得到的最大提升。

 

posted @ 2021-09-15 22:34  zhaohhhh  阅读(45)  评论(0)    收藏  举报