给定一个长度n = 1e5 的数组A,元素是[-1e5, 1e5]之间的
给定一个tot = 1e10,表示最多可操作的次数
一次操作为: 将任意元素 += 1
' 注意,只有加法, 没有减法!!! '
问, 最终数组的 最大相同元素个数
[1, 2, 2, 2, 3] tot = 2
' 你不可以变成[2,2,2,2,2]!!! '
' 操作只有加法,没有减法!!! 最优是:[2,2,2,2, 3] '
即,ans = 4 (只使用一次操作)
' 一定要注意: 操作只有加法, 没有减法!! '
这个题当然是很难的...
因为你很容易,和另一个题型混淆
(给定一个数组,操作是:可以加+1、可以减-1,问让所有元素相同的 最少次数)
' 看似感觉这两个题很相似,但其实毫无关系 '
(这道题是: 在<=limit的限制下,让相同元素 '尽可能的多')
(而那个题是: 没有limit的限制, 而且是让 '所有的元素'都必须相同)
' 而且是最 致命的区别: 这道题只有【加法】,而那个题【加减法都有】 '
(那个题的做法是: [100, 2, 1}
(我们获取他的'中位数' (通过sort 或 动态中位数)
(即 2 是他的中位数, 最少操作是 (100-2)+(2-1)
将数组进行sort, 假设最终数组的ans 所对应的元素是X
1, X一定是最初数组里的元素
2, 最终数组里这ans个X元素,所对应的 最初数组里的元素
I: 要么就是X (即不需要进行操作)
II: 要么一定是Y (且Y<X) ' 即需要X-Y个操作 '
'这个II,是非常重要的!! 因为元素只能变大,不能变小!!'
sort后的A是: [1, 1, 3, 3, 7, 7, 9]
比如我们假设9,是最终的答案(且ans = 4)
那么,答案数组一定是:[1, 1, 3, 9, 9, 9, 9]
' 即假设A[i]是答案, 则我们贪心的、就近原则 '
' 从A[i-1]、A[i-2]、A[i-3]、、、 依次的选 '
' 直到耗尽tot为止 '
这就很像 单调队列(因为对于A[i]来说,能和他匹配的 都是左侧的)
然后,我们需要证明: 单调性
即比如,对于A[5]来说, 他往左走的极限是 A[2]
即{A[2, 3, 4, 5]最终,都可以变成A[5]}
对于任意一个A[>5]来说,他往左走的极限一定是 A[>=2]的
' 只要证明这一点, 就说明 是单调滑动窗口 '
[lef, ...., cur, nex]
lef是对于cur来说,往左走的极限位置
他的操作数 = (A[cur] - A[lef]) + (A[cur] - A[lef+1]) + ... {共cur-lef项}
<= tot
那么对于nex= cur+1来说:
' 假设nex所对应的 左端的,依然是lef '
其操作数 = (A[nex] - A[lef]) + (A[nex] - A[lef+1]
+ .. + (A[nex] - A[cur])
{共cur-lef +1项}
因为A[nex] >= A[cur],
所以可以发现, 此时的操作数 一定是>= 之前的操作数。
即,是单调的。
对于cur的操作数 = (cur-lef)*A[cur] - (A[lef]+A[lef+1]+...+A[cur-1])
这个式子, A[cur]是变量(不用维护他,每次O(1)临时计算即可)
后面这个区间和, 我们动态维护。
sort(A.begin(), A.end()); ' 重点 '
int lef = 0;
LL sum = 0; ' sum为: A[lef]+A[lef+1]+..+A[cur-1] '
' 对应式子: (cur-lef)*A[cur] - sum '
FOR(i, 0, n-1, 1){
while( true ){
LL cur = (i - lef) * 1ll * A[i];
cur -= sum; ' cur 即为 操作数(代价) '
if(cur <= k) break;
sum -= A[lef]; ' 否则,lef右移 以减少代价 '
++ lef;
}
MAX(ans, i - lef + 1);
sum += A[i]; ' 注意别忘了这里!!! sum是cur左侧的区间和'
' 你每次右移动i, sum都要累加的!! '
}
return ans;
贪心、单调队列滑动窗口
最新推荐文章于 2024-01-25 22:06:34 发布