Problem: LCS 01. 下载插件
文章中存在些许公式,建议 PC 端打开观感更佳
思路
通过计算方程一阶导数、二阶导数,以及向上取整的离散性质进行分析
解题过程
- 问题分析+方程推导
-
插件总数为 正整数n ,限制范围1<=n<=105
-
假设我们最优解选择m次用来将带宽加倍, m为非负整数
-
我们将问题拆分为 提速,下载 交错并以下载结束的场景
-
我们使用ai表示提速的次数,bi表示下载的数量,使用Fm表示最优解,使用fk表示有k组交错场景的耗时
-
假设只有1组时:a0=m,b0=n,可以得出:
Fm=f1=m+2mn
-
假设有2组时:[a0,b0]、[a1,b1],可以得出:
f2=a0+2a0b0+a1+2a0+a1b1
- 其中a0+a1=m 且b0+b1=n
- 带入公式可以得出:
f2f2f2f2=a0+a1+2a0b0+2mn−b0=m+2a0b0+2mn−b0=m+2mn+2a0b0−2mb0=f1+2a0b0−2mb0≥f1
- 仅当b0=0 ∣∣ a0=m时f2=f1成立,其中b0=0表示a0和a1连续中间不存在下载部分,a0=m表示不能存在后续[a1,b1]部分,这两种条件均表示,一组较两组更优
- 所以存在两组合法的 提速,下载 场景则一定不是最优解。
-
以此类推,我们可以得出最优解:
Fm=m+2mn
-
而根据题目描述可知,最后一次下载并不是完全可以利用网速,即
nmod2m=0
-
所以可以得出 (公式1):
Fm=m+⌈2mn⌉=⌈m+2mn⌉
- 对于 (公式1),由于包含取整函数⌈.⌉,直接分析其极小值较为复杂,我们将其拆分为两部分:gm=m+2mn, Fm=⌈gm⌉
- 先计算gm的一阶导数以计算极值
- 计算一阶导数
- 将gm转为指数形式:gm=m+n⋅2−m
- 对m部分进行求导:
dmd(m)=1
- 对n⋅2−m部分进行求导:
dmd(n⋅2−m)=n⋅(dmd(2−m))=n⋅(2−m ln 2⋅dmd(−m))=−n ln 2⋅2−m
- 所以gm′=1−n ln 2⋅2m1
- 确定极值
- 令gm′=0,即1−n ln 2⋅2m1=0
- 可以求出m∗=log2(n ln 2)
- 再计算gm的二阶导数以计算极小值
- 求二阶导数
gm′′=dmd(1−n ln 2⋅2−m)=−n ln 2⋅(2−m ln 2⋅dmd(−m))=n(ln 2)2⋅2−m
- 根据二阶导数得知gm′′>0,所以gm是严格凸的
- 所以m∗=log2(n ln 2)是最小值点
- 再根据离散性质确定Fm的最小值
- 由于Fm是gm的取整函数,所以其最小值必定出现在m∗附近
- 所以 (公式2) 为:
m∗=log2(n ln 2)m1=⌊m∗⌋m2=⌈m∗⌉Fmin=min(Fm1,Fm2)
复杂度
使用 (公式1) 计算
- 时间复杂度: O(log n)
- 空间复杂度: O(1)
var leastMinutes = function (n) {
let m = 0;
let min = Infinity;
while (n >> m) {
min = Math.min(min, m + Math.ceil(n / (1 << m)));
m++;
}
return min;
};
使用 (公式2) 计算
- 时间复杂度: O(1)
- 空间复杂度: O(1)
var leastMinutes = function (n) {
let m = Math.log2(n * Math.LN2);
if (m < 0) m = 0;
const l = Math.floor(m);
const r = Math.ceil(m);
return Math.min(
l + Math.ceil(n / (1 << l)),
r + Math.ceil(n / (1 << r))
);
};