文章目录
1. 题目来源
2. 题目解析
贪心+二分答案。
双指针也可以做,单调性可证明,ai
确定的左边连续的 j
,当 ai
变大时,j
单调向后走,不会向左再走了,可以使用双指针。
其实就是个滑动窗口。
最高频数一定可以为原数组数,因为这 k
次操作不是必须全部用完的。
枚举答案 ai
,仅需判断操作 k
次的下,最多能将多少数变成 ai
。且一定是操作 ai
前面连续的一段,因为只能 +1,所以后面的大于 ai
的数不在考虑范围内,且从左向 ai
变的话一定不如从 ai
向左变好。
假设在 j
坐标停下,[j, i]
区间中的数都能变成 ai
,那么实际上操作的次数之和就是
(
a
[
i
]
−
a
[
j
]
)
+
(
a
[
i
]
−
a
[
j
+
1
]
)
+
.
.
.
+
(
a
[
i
]
−
a
[
i
−
1
]
)
+
(
a
[
i
]
−
a
[
i
]
)
(a[i]-a[j])+(a[i]-a[j+1])+...+(a[i]-a[i-1])+(a[i]-a[i])
(a[i]−a[j])+(a[i]−a[j+1])+...+(a[i]−a[i−1])+(a[i]−a[i]),其实就是 (i-j+1)*a[i]+s[j:i]
。其中 s[j:i]
就是 a[j]+a[j+1]+..+a[i]
区间和,利用前缀和数组进行优化。
时间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
空间复杂度:
O
(
n
)
O(n)
O(n)
代码:
二分, O ( n l o g n ) O(nlogn) O(nlogn),比较容易分析。
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
typedef long long LL;
sort(nums.begin(), nums.end());
int n = nums.size();
vector<LL> sum(n + 1);
for (int i = 1; i <= n; i ++ ) sum[i] = sum[i - 1] + nums[i - 1];
int res = 0;
for (int i = 1; i <= n; i ++ ) {
int l = 1, r = i;
while (l < r) {
int mid = l + r >> 1;
if (nums[i - 1] * (i - mid + 1ll) - (sum[i] - sum[mid - 1]) > k) l = mid + 1;
else r = mid;
}
res = max(res, i - r + 1);
}
return res;
}
};
双指针,
O
(
n
)
O(n)
O(n),需要特别分析 i
向后移动的过程中,j
指针不会向后移动。很明显的滑动窗口了。
class Solution {
public:
int maxFrequency(vector<int>& nums, int k) {
typedef long long LL;
sort(nums.begin(), nums.end());
int n = nums.size();
vector<LL> s(n + 1);
for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + nums[i - 1];
int res = 0;
for (int i = 1, j = 1; i <= n; i ++ ) {
while ((i - j + 1ll) * nums[i - 1] - (s[i] - s[j - 1]) > k) j ++ ;
res = max(res, i - j + 1);
}
return res;
}
};