问题描述
小M的任务是调整一只股票的价格,使其符合一个给定的正整数序列p0,p1,…,pn−1p0,p1,…,pn−1,并且要确保每个月的股票上涨系数不能超过某个百分比kk。上涨系数定义为当月价格相对于之前所有月份价格总和的比例。如果上涨系数超过k%k%,就需要对某些月份的价格进行调整,以尽量减少对整体序列的变更。
你需要输出在保证上涨系数不超过k%k%的情况下,最少需要增加的价格总和。
测试样例
样例1:
输入:
n = 5,k = 12,p = [1000, 34, 12, 27, 131]
输出:19
样例2:
输入:
n = 4,k = 10,p = [100, 1, 1, 1]
输出:0
样例3:
输入:
n = 6,k = 15,p = [500, 50, 30, 20, 10, 60]
输出:0
问题理解
你需要调整股票价格序列,使得每个月的上涨系数不超过给定的百分比 k
。上涨系数定义为当月价格相对于之前所有月份价格总和的比例。如果某个上涨系数超过了 k%
,你需要增加某些月份的价格,以尽量减少对整体序列的变更。
数据结构选择
- 我们可以使用一个数组来存储每个月的价格。
- 使用一个变量来记录之前所有月份价格的总和。
算法步骤
-
初始化:
- 初始化一个变量
total_sum
来记录之前所有月份价格的总和。 - 初始化一个变量
additional_cost
来记录需要增加的价格总和。
- 初始化一个变量
-
遍历价格序列:
- 对于每个月的价格
p[i]
,计算当前的上涨系数current_ratio
,即p[i] / total_sum
。 - 如果
current_ratio
超过了k%
,则需要调整当前月份的价格。
- 对于每个月的价格
-
调整价格:
- 计算需要增加的价格
needed_increase
,使得current_ratio
不超过k%
。 - 更新
additional_cost
和total_sum
。
- 计算需要增加的价格
-
返回结果:
- 返回
additional_cost
作为最终结果。
- 返回
解题过程
- 二分查找初始化:我们初始化二分查找的左右边界,左边界
l
为0,右边界r
为一个很大的数(例如109)。 - 二分查找过程:在每次循环中,我们计算中间值
mid
,并判断在增加mid
价格的情况下,是否所有月份的上涨系数都不超过k%。 - **判断函数
ok
**:这个函数用于判断在增加x
价格的情况下,是否所有月份的上涨系数都不超过k%。具体来说,我们计算每个月的价格总和,并检查每个月的上涨系数是否满足条件。 - 调整边界:如果当前的
mid
满足条件,则说明可能存在更小的增加价格,因此我们将右边界r
调整为mid
;否则,我们将左边界l
调整为mid + 1
。 - 返回结果:最终,当
l
等于r
时,l
即为最小的增加价格总和。
复杂度分析
- 时间复杂度:二分查找的时间复杂度为O(log(109)),每次判断的时间复杂度为O(n),因此总的时间复杂度为O(nlog(109))。
- 空间复杂度:除了输入数组外,我们只使用了常数级别的额外空间,因此空间复杂度为O(1)。
知识点扩展
- 二分查找:二分查找是一种高效的查找算法,适用于在有序数组中查找特定值或满足特定条件的值。在本题中,我们通过二分查找来找到最小的增加价格总和。
- 贪心算法:虽然本题没有直接使用贪心算法,但贪心算法的思想在判断函数
ok
中有所体现,即我们每次都尽量满足条件,而不考虑全局最优。
代码实现
Python
def solution(n:int, k:int, p:list)->int:
assert n == len(p)
def ok(x, n, k, a):
s = x + a[0]
for i in range(1, n):
if 100 * a[i] > s * k:
return False
s += a[i]
return True
l, r = 0, int(1e9)
while l < r:
mid = (l + r) // 2
if ok(mid, n, k, p):
r = mid
else:
l = mid + 1
return l
if __name__ == '__main__':
print(solution(n = 5,k = 12,p = [1000, 34, 12, 27, 131]) == 19)
print(solution(n = 4,k = 10,p = [100, 1, 1, 1]) == 0)
print(solution(n = 6,k = 15,p = [500, 50, 30, 20, 10, 60]) == 0)
C++
#include <iostream>
#include <vector>
int solution(int n, int k, std::vector<int>& p) {
assert(n == p.size());
auto ok = [](int x, int n, int k, const std::vector<int>& a) -> bool {
int s = x + a[0];
for (int i = 1; i < n; ++i) {
if (100 * a[i] > s * k) {
return false;
}
s += a[i];
}
return true;
};
int l = 0, r = 1e9;
while (l < r) {
int mid = (l + r) / 2;
if (ok(mid, n, k, p)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
int main() {
std::vector<int> arr1 = {1000, 34, 12, 27, 131};
std::vector<int> arr2 = {100, 1, 1, 1};
std::vector<int> arr3 = {500, 50, 30, 20, 10, 60};
std::cout << (solution(5, 12, arr1) == 19) << std::endl;
std::cout << (solution(4, 10, arr2) == 0) << std::endl;
std::cout << (solution(6, 15, arr3) == 0) << std::endl;
return 0;
}
关键步骤解释
-
**函数
solution
**:- 参数
n
表示月份的数量,k
表示上涨系数的百分比,p
是价格序列。 - 使用
assert
确保n
等于p
的长度。
- 参数
-
**Lambda 函数
ok
**:- 用于检查在增加
x
的价格后,是否所有月份的上涨系数都不超过k%
。 - 初始化
s
为x + a[0]
,然后遍历价格序列,检查每个价格是否满足条件。
- 用于检查在增加
-
二分查找:
- 初始化
l
为 0,r
为1e9
。 - 使用二分查找来找到最小的
x
,使得ok(x, n, k, p)
为true
。
- 初始化
-
**主函数
main
**:- 定义测试样例并调用
solution
函数进行测试。
- 定义测试样例并调用