鹰蛋总结
本篇文章是 based on 这篇的,感谢对我的帮助:
注意,这个文章不是正解。
这篇文章的更新日志:
2024/4/13:开坑。
好,我们进入正题吧。
问题简述
有一栋 \(n\) 层的大楼,\(m\) 个一样的鹰蛋。现在你想要知道至少几层楼才能摔坏(如果都没有摔坏算 \(n+1\)),摔坏的蛋不能用了。
简单版的 \(n,m\le 1000\);加强版 \(n,m\le 10^{18}\)。
其他算法
研究一个问题,有两个方面:
-
在保证复杂度的时候的最佳方案能想到啥?
-
在保证最优方案的时候的最优复杂度能想到啥?
在最优化之前,我们有什么 intuitive 的算法?
如果是你,你想到的第一种是啥?
你会二分
如果二分高度,很容易发现我们可以很快确定高度,但是鹰蛋也许不够。一个 \(n\) 层楼的,\(m\) 要多少呢?
答案是 \(m\ge \lceil \log_2(n+1)\rceil\)。容易发现这个是不考虑鹰蛋个数,或者是无限多个的时候的最佳方案。
所以,我们可尝试把 \(m\) 缩小到 \(\lceil \log_2(10^18)\rceil\) 约 \(60\) 的级别?
你会你个一个扫
如果 \(m\) 有限制,比如说 \(m=1\),我们直接从 \(1\) 到 \(n\) 顺次尝试,这样 \(n\) 次一定可以得出结果。事实上,这个是 \(m=1\) 的最佳方案。
你会 \(m\) 个 \(m\) 个扫
我们有 \(m\) 个鹰蛋呢!为什么不这样:第一个鹰蛋测试 \(1,m+1,2m+1,\cdots\),第二个鹰蛋测试 \(2,m+2,2m+2,\cdots\),这样不就可以比一个一个扫方便很多!
这个方案要什么复杂度?容易发现,我们先测试第一个鹰蛋,如果它挺过了 \(i\) 层,\(i+m\) 层破了,一定在 \(i+1\sim i+m\) 之间。这个实验 \(m-1\) 次即可。所以我们把最有次数缩小到 \(\lceil \frac{n}{m}\rceil+m-1\)。
你会把 \(m\) 个 \(m\) 个扫和二分结合
前面很明显可以优化啊。最后的 \(i+1\sim i+m\) 即 \(m-1\) 个数,我们用 \(m-1\) 个鹰蛋,显然可以只用 \(\mathcal{O}(\log_2(m-1))\) 次确定答案。
虽然说这个只能优化一点点,但是又没有什么启发?
你会把 \(1\) 个 \(1\) 个扫和二分结合
二分的弊端在于,我们没有足够的鹰蛋。如果把\(1\) 个 \(1\) 个扫和二分配合起来,会更优吗?
我们留下一个鹰蛋,其他的鹰蛋去二分。有了 \(m-1\) 个鹰蛋的努力,我们范围缩小到了 \(\frac{n}{2^{m-1}}\)。这个东西我们用最有一个鹰蛋 \(1\) 个 \(1\) 个扫就可以了。
容易发现,这个在 \(n\) 比较大的时候比前面优很多。
你会把 \(1\) 个 \(1\) 个扫,\(x\) 个 \(x\) 个扫和二分结合
前面的经验告诉我们,除以一个数贡献很大,结合算法很好。
那么,全部结合呢?
我们先用 \(m-2\) 个鹰蛋二分,然后用一个鹰蛋来“间隔”地测(我们的 \(m\) 个 \(m\) 个扫就是间隔为 \(m\)),最后用一个鹰蛋 \(1\) 个 \(1\) 个扫,这样会不会更优?
当然你会说,为啥必须是 \(m-2\) 个?我们先不考虑这个,考虑后半部分最优是啥,即,间隔是多少最好?问出这个问题,说明你已经不拘泥于二分。
我们做一个数学分析。设分成 \(x\) 份,令 \(sz=n/2^{m-2}\),我们其实要最小化 \(f(x)=\frac{sz}{x}+x-1\)。根据小学一年级,这个是对勾函数,在 \(x=\sqrt{sz}\) 时最优。
我都说了,你已经不拘泥于二分-pre
为啥是 -pre 呢,因为这个没啥用(至少我不会)。
不是二分的第一个想到的是三分。三分的常见用法是在求一个函数在 \([l,r]\) 中的极值。因此,我想到了:如果我们能把每一种鹰蛋的强度的输入,设计一种算法,使输出的函数都是不一样的凹/凸函数?
如果我们可以这样,怎么求鹰蛋硬度呢?我们要确定一个 \(n\) 级的函数,取 \(n+1\) 个点,然后做插值就可以了。因此,我们要最小化的是我们算法生成的函数的级。
我都说了,你已经不拘泥于二分
把二分替代掉,用 \(k\) 分。这里的 \(k\) 分不是系统意义上的 \(k\) 分。我们来想一个事情:
假如你是和一个很邪恶的交互库玩这个“游戏”,它每一次可以通过你的询问(即实验)判断鹰蛋碎不碎。他想要干啥?他要:
-
我尽量让你鹰蛋碎,这样你就没有鹰蛋实验了!
-
我尽量让你实验很多次,这样你就没耐心了!
二分的弊端是什么?我们来看看碎和不碎的区别:
-
碎了,那么可能的硬度区间除以 \(2\),少了一个鹰蛋。
-
没碎,那么可能的硬度区间除以 \(2\),没少一个鹰蛋。
邪恶的交互库一定会让你碎的!那么 \(k\) 分的优点是啥?举 \(3\) 分的例子吧:
-
碎了,那么可能的硬度区间除以 \(3\),少了一个鹰蛋。
-
没碎,那么可能的硬度区间变成原来的 \(2/3\),没少一个鹰蛋。
也就是……碎了鹰蛋,但是可以除以 \(3\),\(3>2\);没碎鹰蛋,一还可以继续实验啊!这就可以让邪恶的交互库发愁了。
但是显然 \(3\) 分是不可取的,因为 \(3^m\) 如果小于 \(n\),就不行。那么我们怎么选择 \(k\) 呢?我们要 \(k^m\ge n\),就要取 \(k=\sqrt[m]{n}\)。如果是 \(n=10^{18},m=12\),\(k=32\) 比较才能确定可行。这个时候最劣操作次数是啥呢?是 \(\log_{32/31} 10^{18}=1306\)!这个是不是突破了很多?
注:\(10^{18},12\) 的例子就是上面链接 link 1 里面的。
你会 \(k\) 分法和 \(1\) 个 \(1\) 个扫结合
这边的 \(k\) 分才是系统意义上的 \(k\) 分。用 \(k\) 分,确定一个数的最多操作次数是 \((k-1)\times \log_{k}(n)\),也就是说,缩小成 \(1/k^x\),我们要 \((k-1)\times x\) 次。至于这个,可以用三分法来理解。
我们来算一下\(k\) 分法和 \(1\) 个 \(1\) 个扫结合的最劣方案数吧!同样,\(k=\sqrt[m-1]{n}\) 最优。答案是:\((m-1)\times (k-1)+\frac{n}{k^{m-1}}\)。如果把 \(n=10^{18},m=12\) 带入,\(k=43\),因此 \(11\times 42+10^{18}/(43^{11})=463\)!这个,更接近答案了!
你还有别的想法
你还有什么做法呢?比如说,不同方法的结合?
浙公网安备 33010602011771号