中华建设杂志网站记者绍兴网站建设报价
Problem: 3495. 使数组元素都变为零的最少操作次数
文章目录
- 整体思路
- 完整代码
- 时空复杂度
- 时间复杂度:O(Q * log R_max)
- 空间复杂度:O(1)
整体思路
这段代码旨在高效地处理一系列查询 queries。对于每个查询 [l, r],它需要计算一个特定的值,然后将所有查询的结果累加起来。问题的核心在于理解辅助函数 preSum(x) 的作用以及主函数中 Math.max((sum + 1) / 2, max) 这条计算公式的含义。
算法的整体思路可以分解为以下几个步骤:
-
定义“成本”函数
f(k):- 代码隐含地为每个正整数
k定义了一个“成本”或“值”。这个成本由preSum函数的内部逻辑决定。 preSum函数将正整数1, 2, 3, ...分成基于4的幂次的区间:[1, 3](即[4^0, 4^1 - 1])[4, 15](即[4^1, 4^2 - 1])[16, 63](即[4^2, 4^3 - 1])- …
- 第
i个区间是[4^(i-1), 4^i - 1]。
- 所有落在第
i个区间的整数k,其“成本”f(k)都被定义为i。
- 代码隐含地为每个正整数
-
高效计算“成本”的前缀和:
preSum(x)函数的作用是计算从 1 到x所有整数的“成本”之和,即Σ f(k)forkfrom 1 tox。- 它通过
while循环逐个处理上述的分层区间。在每次循环中,它计算出[1, x]范围内有多少个数(count)落在了当前处理的第i个区间,然后将count * i累加到总和ans中。这种分层累加的方法远比逐个计算f(k)要高效。
-
处理查询
[l, r]:minOperations方法遍历每个查询。对于一个查询[l, r]:
a. 计算区间总成本sum:利用前缀和的思想,区间[l, r]的总成本可以通过preSum(r) - preSum(l - 1)快速计算得出。
b. 计算区间最大成本max:同样利用前缀和,区间内单个元素的最大成本必然是f(r),因为成本函数f(k)是单调不减的。f(r)的值可以通过preSum(r) - preSum(r - 1)计算出来。
c. 应用特定公式:根据问题的具体要求(这通常与游戏理论或某种优化目标有关),将计算出的sum和max代入公式Math.max((sum + 1) / 2, max)来得到该查询的结果。(sum + 1) / 2是一种计算ceil(sum / 2.0)的整数方法。
-
累加总结果:
- 将每个查询得到的结果累加到
ans中,最终返回总和。
- 将每个查询得到的结果累加到
完整代码
class Solution {/*** 处理一系列查询,并返回结果的总和。* @param queries 一个二维数组,每个子数组 [l, r] 代表一个查询区间。* @return 所有查询结果的累加和。*/public long minOperations(int[][] queries) {long ans = 0;// 遍历每一个查询for (int[] query : queries) {int l = query[0];int r = query[1];// 使用 preSum 函数计算区间 [l, r] 内所有数字的“成本”总和。// 这是经典的前缀和用法。long sum = preSum(r) - preSum(l - 1);// 计算区间 [l, r] 内最大的单个“成本”。// 由于“成本”函数是单调不减的,所以最大成本就是 f(r)。long max = preSum(r) - preSum(r - 1);// 应用问题的特定公式来计算当前查询的结果。// (sum + 1) / 2 是 ceil(sum / 2.0) 的整数实现。ans += Math.max((sum + 1) / 2, max);}return ans;}/*** 计算从 1 到 x 的所有整数的“成本”之和。* “成本” f(k) = i,其中 4^(i-1) <= k < 4^i。* @param x 上限整数* @return 成本的前缀和*/private long preSum(int x) {// 如果 x 为 0 或负数,前缀和为 0if (x <= 0) return 0;long ans = 0; // 累加结果long p = 1; // 当前处理区间的起始值,p = 4^(i-1)int i = 1; // 当前处理区间的“成本”// 循环直到区间的起始值 p 超过 xwhile (p <= x) {// 计算 [1, x] 与当前成本区间 [p, 4*p - 1] 的交集中的元素数量。// Math.min(x, p * 4 - 1) 是交集的上界。long count = Math.min(x, p * 4 - 1) - p + 1;// 将这 count 个元素的总成本 (count * i) 累加到 ansans += count * i;// 移至下一个成本区间i++;p *= 4;}return ans;}
}
时空复杂度
时间复杂度:O(Q * log R_max)
-
preSum(x)函数分析:- 该函数的核心是一个
while循環。 - 循环变量
p每次迭代都乘以 4 (1, 4, 16, 64, ...)。这是一个指数级增长。 - 因此,循环的次数与
log4(x)成正比。 - 所以,
preSum(x)函数的时间复杂度为 O(log x)。
- 该函数的核心是一个
-
minOperations函数分析:- 该函数的主体是一个
for循环,它遍历所有的查询。设查询的数量为Q。 - 在每次循环中,它会调用
preSum函数三次。 - 调用的参数最大为
r。设所有查询中r的最大值为R_max。 - 那么,单次查询的处理时间为
3 * O(log R_max),即 O(log R_max)。
- 该函数的主体是一个
综合分析:
总的时间复杂度是 (查询数量) * (单次查询的时间),即 O(Q * log R_max)。
空间复杂度:O(1)
- 存储分析:
minOperations函数和preSum函数在执行过程中都只使用了少数几个基本类型的变量(如ans,l,r,sum,max,p,i,count)。- 这些变量的数量是固定的,不随查询数量
Q或查询范围R_max的大小而改变。
综合分析:
算法没有使用任何与输入规模成比例的额外数据结构。因此,其额外辅助空间复杂度为 O(1)。
