惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

P
Proofpoint News Feed
Microsoft Azure Blog
Microsoft Azure Blog
Jina AI
Jina AI
博客园_首页
宝玉的分享
宝玉的分享
The Cloudflare Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
量子位
T
Tailwind CSS Blog
雷峰网
雷峰网
Blog — PlanetScale
Blog — PlanetScale
Last Week in AI
Last Week in AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Hugging Face - Blog
Hugging Face - Blog
月光博客
月光博客
罗磊的独立博客
F
Fortinet All Blogs
酷 壳 – CoolShell
酷 壳 – CoolShell
Stack Overflow Blog
Stack Overflow Blog
J
Java Code Geeks
V
V2EX
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The GitHub Blog
The GitHub Blog
Apple Machine Learning Research
Apple Machine Learning Research
博客园 - 聂微东
U
Unit 42
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
D
Docker
阮一峰的网络日志
阮一峰的网络日志
I
InfoQ
Simon Willison's Weblog
Simon Willison's Weblog
D
DataBreaches.Net
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
I
Intezer
Scott Helme
Scott Helme
B
Blog
M
MIT News - Artificial intelligence
K
Kaspersky official blog
H
Help Net Security
V
Vulnerabilities – Threatpost
C
CXSECURITY Database RSS Feed - CXSecurity.com
Engineering at Meta
Engineering at Meta
博客园 - 【当耐特】
L
Lohrmann on Cybersecurity
P
Privacy & Cybersecurity Law Blog
Project Zero
Project Zero
The Hacker News
The Hacker News
B
Blog RSS Feed
T
Tor Project blog

Long Luo's Life Notes

夏至日测地球:利用太阳影子计算地球半径 太阳温度是怎么计算出来的? 《大象的时间,老鼠的时间》读书笔记:生命节奏背后的数学规律 小港流到哪里去? 如何用一根棍子测出地球有多大?复刻埃拉托色尼的春分实验 2007江苏高考数学第20题解析:一道通向黄金分割数的数列压轴题 Google经典面试题: 鸡蛋应该怎么扔? 2010年江苏高考数学压轴题解析:巧用余弦定理与数学归纳法 2011年清华大学自主招生数学题解析:一道经典数列题的解法与思路 2011年清华大学自主招生数学题解析:一道经典数列题的解法与思路 2006年江西高考理科数学压轴题解析:递推、放缩与不等式结构 2006年江西高考理科数学压轴题解析:递推、放缩与不等式结构 一道初中数学极值题的多种解法:柯西不等式、几何法、函数法详解 扔几个骰子,怎么算出期望?——拼多多校招笔试算法题的数学故事 斯特林公式(Stirling's Formula):我一个阶乘表达式,怎么就和圆扯上关系了呢? 我爱做题:2010年江西高考理科数学压轴题 热机的效率上限在哪里?解析卡诺循环(Carnot Cycle) 为什么 2024 年会有 366 天? 数学之美:几何视角下的高斯积分(Gaussian Integral) 从最小二乘法到正态分布:高斯是如何找到失踪的谷神星的? 正态分布(Normal Distribution)公式为什么长这样? 高速公路编号背后的数学密码 2024阿里巴巴全球数学竞赛预选赛试题及解答 库函数 (libm) 是如何计算三角函数值的? payne hanek 归约算法 音乐背后的数学 素描背后的物理 cody waite 浮点数 Remez Algorithm 参数归约算法(Argument Range Reduction):如何在浮点数环境下计算超大数字的三角函数值? 素描背后的数学 发生在计算机内存里的进化:解密遗传算法(Genetic Algorithm) CORDIC算法:一种高效计算三角函数值的方法 墨卡托的魔术:地图是如何欺骗你的眼睛的? PID 算法到底在干什么?工程师最常用的控制方法 解密卡尔曼滤波(Kalman Filter)算法:深入解析卡尔曼滤波算法原理与在线可视化实例 从记忆到洞察:轻松掌握泰勒展开式(Taylor Series)的记忆技巧 哪个更大呢? $2^{100!}$ 还是 $2^{100}!$ ? Google经典编程竞赛题:计算 $(3 + \sqrt{5})^n$ 的小数点前三位数 手写数字识别:解码机器学习的背后的数学原理 The Answers of MRI Tutorial Videos gdb 操作指南 Linux 网络命令指南 贝塞尔曲线(Bezier Curve):优雅背后的数学原理 LeetCode 380. Insert Delete GetRandom O(1) Data Structures: Thought Process from HashMap to HashMap + Array LeetCode 2475. 数组中不等三元组的数目 2种 O(n) 时间复杂度算法 LeetCode 947. Most Stones Removed with Same Row or Column It is Literally a Graph: DFS and Union Find LeetCode 295. Find Median from Data Stream Two Heaps with the Follow Ups LeetCode 295. Find Median from Data Stream Two Heaps with the Follow Ups LeetCode 1668. 最大重复子字符串 不用API,比KMP更易理解简洁优雅的暴力解法 LeetCode 334. Increasing Triplet Subsequence Why Greedy Works? LeetCode 迷宫问题(The Maze)
拼多多校招笔试算法题:一行公式搞定“多多的魔术盒子”
2025-01-26 · via Long Luo's Life Notes

By Long Luo

众多IT大厂中拼多多虽然工作强度很大,但在给钱方面非常大方。大厂给的钱多,但要求也高。下面就来挑战拼多多 2020 年校招笔试算法题 1 中第一题:多多的魔术盒子 2 ,看看难度如何。

多多的魔术盒子

多多鸡有 \(N\) 个魔术盒子(编号 \(1 \sim N\) ),其中编号为 \(i\) 的盒子里有 \(i\) 个球。多多鸡让皮皮虾每次选择一个数字 \(X\) ( \(1 \le X \le N\) ),多多鸡就会把球数量大于等于 \(X\) 个的盒子里的球减少 \(X\) 个。 通过观察,皮皮虾已经掌握了其中的奥秘,并且发现只要通过一定的操作顺序,可以用最少的次数将所有盒子里的球变没。

那么请问聪明的你,是否已经知道了应该如何操作呢?

  • 时间限制:

    • C/C++ 1秒,其他语言 2 秒
  • 空间限制:

    • C/C++ 256M,其他语言 512M
  • 输入描述:

    • 第一行,有 \(1\) 个整数 \(T\) ,表示测试用例的组数。 ( \(1 \le T \le 100\) )
    • 接下来 \(T\) 行,每行 \(1\) 个整数 \(N\) ,表示有 \(N\) 个魔术盒子。 ( \(1 \le N \le 1,000,000,000\) )
  • 输出描述:

    • \(T\) 行,每行 \(1\) 个整数,表示要将所有盒子的球变没,最少需要进行多少次操作。
  • 输入例子 1 :

    1
    2
    3
    4
    3
    1
    2
    5
  • 输出例子 1 :

    1
    2
    3
    1
    2
    3

最少的操作次数该怎么做?

根据题意,我们关键是要找到最少次数这个方法,那如何操作才能使用最少次数呢?

当面对复杂问题时,我们需要从简单情况入手,分析其中规律,找到突破口。

  1. \(N = 1\) 时,显然选择 \(X = 1\) ,需要 \(1\) 次操作;
  2. \(N = 2\) 时,可以先选择 \(X = 1\) ,再选择 \(X = 2\) ,需要 \(2\) 次操作;
  3. \(N = 3\) 时,只有先选择 \(X = 2\) ,再选择 \(X = 1\) ,最少需要 \(2\) 次操作;
  4. \(N = 4\) 时,可以先选择 \(X = 2\) 或者 \(X = 3\) ,最少需要 \(3\) 次操作;

通过分析发现,每次操作之后,球的数量都会动态变化。如果每次都选择中间的数字,这样每次操作之后,如果 \(N\) 为奇数的话,可以变成 \(2\) 个对称相同的数组, \(N\) 为偶数的话,则 \(2\) 个数组中元素值会相差 \(1\) ,再选择元素值更多的数组进行消除,这样可以实现操作次数最少

实现代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private static int leastTimes_power(int n) {
int ans = 1;

for (int i = 0; i <= Math.sqrt(n); i++) {
if (Math.pow(2, i) <= n) {
ans = i;
} else {
break;
}
}

return ans + 1;
}

为什么每次选取中间的数字进行操作次数最少呢?下面我们就来严谨的证明下!

为什么这样操作次数最少?

对于 \(N\) 个魔法盒子中的球,可以视为一个数组 \(A\) :对于 \([1, 2, 3 \cdots, N - 1, N]\) 。第一次操作选择哪个数呢?为不失普遍性,假设选择了任意正整数 \(k\) ,这样序号大于等于 \(k\) 的盒子中球的数量都要减去 \(k\) ,那么减少之后的数组 \(B\) 为: \([1, 2, 3, \cdots , k-1, 0, 1, 2, \cdots , N - k]\)

那么第二次操作选哪个数呢?分析数组 \(B\) ,在第一次操作选择了 \(k\) 之后,数组 \(B\)\(0\) 为中心,分为 \(2\) 个部分,左边是 \([0, 1, \cdots , k - 1]\) ,右边是 \([1, 2, \cdots , N - k]\) 。这里有没有想到 二分查找法 ?问题相同但规模更小!

这里又可以分成 \(3\) 种情况:

  1. 如果 \(k - 1 = N - k\) ,左右两边数字相同,那么只要能消除左边部分,右边也可以消除;
  2. 如果 \(k - 1 > N - k\) ,左边元素比右边多,只要能够消除掉左边的部分,右边的自然也会消除;
  3. 如果 \(k - 1 < N - k\) ,左边元素比右边少,同理可得,只需要消除右边部分。

综合,每次选择 \(k\) 之后,数组可以拆分为两个部分,而答案只和其中元素更多的部分有关。设函数 \(f(N)\) 表示 \(N\) 时为最少操作次数,不难得到下列方程

\[ f(N) = \min (\max (f(1), f(N-2)), \max(f(2), f(N-3)), \cdots, \max (f(\frac{N}{2}, \frac{N}{2}))) + 1 \tag{1} \label{1} \]

可以直观看出函数 \(f(N)\) 是递增的,但既然是证明就需要严谨,下面我们来简单证明下:

证明 \(f(N)\) 是递增函数

对于 \(N\) 个盒子,在选择了 \(k\) 之后,我们可以得到下列表达式:

\[ f(N) = 1 + \min_{1 \le k \le N} \max \left( f(k-1), f(N-k) \right) \tag{2} \label{2} \]

要证对于任意正整数 \(N\) ,有 \(f(N+1) \ge f(N)\)

我们可以使用归纳法:

  1. 显然 \(f(1) = 1\) ,故命题对 \(N = 1\) 成立。

  2. 假设对所有 \(m \le N\) 都有 \(f(m+1) \ge f(m)\)

比较 \(f(N+1)\)\(f(N)\) 。由递推式,

\[ \begin{aligned} f(N) &= 1 + \min_{1 \le k \le N} \max \left( f(k-1), f(N-k) \right)), \\ f(N+1) &= 1 + \min_{1 \le k \le N + 1} \max \left( f(k-1), f(N+1-k) \right)) \end{aligned} \]

对任意固定的 \(k\) ( \(1 \le k \le N\) ),由归纳假设可得: \(f(N+1-k) \ge f(N-k)\)

故有对于任意 \(k\) 都满足:

\[ \max(f(k-1),f(N+1-k)) \ge \max(f(k-1),f(N-k)) \tag{3} \label{3} \]

因此将两边对 \(k\) 取最小得到

\[ \min_{1 \le k \le N} \max \left( f(k-1), f(N+1-k) \right) \ge \min_{1 \le k \le N} \max \left( f(k-1), f(N-k) \right) \]

两边同时 \(+1\) 可得:

\[ 1 + \min_{1 \le k \le N} \max \left( f(k-1), f(N+1-k) \right) \ge 1 + \min_{1 \le k \le N} \max \left( f(k-1), f(N-k) \right) \]

因此有:

\[ f(N+1) = 1 + \min_{1 \le k \le N + 1} \max \left( f(k-1), f(N+1-k) \right) \ge 1 + \min_{1 \le k \le N} \max \left( f(k-1), f(N+1-k) \right) \]

结合可得:

\[ f(N+1) \ge 1 + \min_{1 \le k \le N} \max \left( f(k-1), f(N-k) \right)) = f(N). \]

这样就证明了对于所有正整数 \(N\) 都有 \(f(N+1) \ge f(N)\)

根据上述证明 \(f(N)\) 是递增函数,那么公式 \(\eqref{1}\) 可以化简为:

\[ f(N) = \min(f(N-2), f(N-3), \cdots , f(\frac{N}{2})) + 1 = f(\frac{N}{2}) + 1 \tag{4} \label{4} \]

容易得出:

\[ \begin{aligned} f(N) &= f(\frac {N}{2}) + 1 \\ f(\frac {N}{2}) &= f(\frac {N}{4})+1 \\ &\cdots \\ f(1) &= 1 \end{aligned} \]

等式两边相加,合并同类项最终可以得到:

\[ f(N) = \log_{2}{N} + 1 \]

则代码实现为:

1
2
3
private static int leastTimes(int n) {
return (int) (Math.log(n) / Math.log(2)) + 1;
}

总结

这道校招题难度并不大,也就 Medium 难度吧!解题关键在于读懂题意,通过分析题意找到如何实现最少次数操作。在没有头绪的情况下,可以先从最简单的情况开始分析,找到突破口。

参考资料


  1. 拼多多2020校招部分编程题合集↩︎

  2. 多多的魔术盒子↩︎