题解/算法 {2968. 执行操作使频率分数最大}
@LINK: https://leetcode.cn/problems/apply-operations-to-maximize-frequency-score/
;
{ 做法1 (不好)
将序列排序, 遍历每个元素A[i]
, 让他往左右扩展 选择代价小的方向, 最终得到[l,r]
(即说明A[l...r]
都等于A[i]
) 更新答案;
然后优化, 即令A[i+1]
的答案是ll,rr
, 考虑如何从[l,r]
更新到[ll,rr]
; 首先, 这个思路其实是不对的, 这并不是滑动窗口! 因为无法证明l,ll; r,rr
是存在单调关系的;
但如果硬是这么做, 也是可以转换的;
[l...i...r]
首先转换到[l...i+1...r]
, 原来的A[i+1...r]
这些元素的代价 会都减去一个A[i+1]-A[i]
; 而原来的A[l...i]
这些元素的代价 都会加上一个A[i+1]-A[i]
; 然后此时[l...i+1...r]
她可能是不合法的 (即代价溢出了) 因为你把所有<=A[i]
的元素 都加上了一个代价
1: 如果溢出了, 一直把A[l]
给删除掉; (比如1,100; K=0
, 原来在1
时是[0,0]
你现在变成[0,1]
代价溢出了);
2: A[l]与A[i+1]
的差距 可能非常大, 如果他比A[r+1]-A[i+1]
都大 一直把A[l]
给删掉; (比如1,100,101,102...; K=100
, 原来在1
时是[0,2]
(代价100), 现在在100
是[0,2]
(虽然代价没有溢出) 但是由于abs(1-100)
太大 要把1
给删除掉 因为[100,101,102,...]
才是答案;
3: 此时的[l,...,i+1,...,r]
是合法的, 再把他进行扩张;
int maxFrequencyScore(vector<int>& A, long long K) {
sort( A.begin(), A.end());
int N = A.size();
int l, r;
auto Padding = [&]( int _cur){
ASSERT_(l<=_cur && r>=_cur);
while( l>0 || r<N-1){
if( l>0 && r<N-1){
auto ll = abs(A[l-1] - A[_cur]), rr = abs(A[r+1] - A[_cur]);
// 注意比較的是`ll,rr`的大小, 而不是`A[l-1],A[r+1]`的大小 (因為顯然`A[l-1]<=A[r+1]`);
if( ll<=rr && abs(A[l-1] - A[_cur])<=K){
K -= abs(A[l-1] - A[_cur]); --l;
}
else if( ll>rr && abs(A[r+1] - A[_cur])<=K){
K -= abs(A[r+1] - A[_cur]); ++r;
}
else{ break;}
}
else if( l > 0){
if( abs(A[l-1] - A[_cur])<=K){
K -= abs(A[l-1] - A[_cur]); --l;
}
else{ break;}
}
else if( r < N-1){
if( abs(A[r+1] - A[_cur])<=K){
K -= abs(A[r+1] - A[_cur]); ++r;
}
else{ break;}
}
else{ break;}
}
};
int ANS = 1;
for( int i = 0; i < N; ++i){
if( i == 0){
l = 0, r = 0;
Padding( 0);
}
else{
K += (r - (i-1)) * 1LL * (A[i]-A[i-1]);
K -= ((i-1) - l + 1) * 1LL * (A[i]-A[i-1]);
r = max( i, r); // 如果`r==i-1`, 此時要把他更新為`i`;
while( K<0 && l<i){ K += abs(A[l]-A[i]); ++l;}
//> shrink `l` (注意比較的是`A[r+1]` 而不是`A[r]`, 否則會超時);
while( r+1<N && l<i && abs(A[l]-A[i])>abs(A[r+1]-A[i])){
K += abs(A[l] - A[i]); ++ l;
}
Padding( i);
}
ANS = max( ANS, r-l+1);
}
return ANS;
}
}
上面做法是碰巧的… 因為[l,r] -> [ll,rr]
並沒有單調性;
正確做法是: 排序以後, 假如最終的眾數是T, 那麼他在序列裡 一定是形如: ??? T...T ???
, 也就是 她是連續的一段; 即他是一個區間, 因此考慮他是否滿足滑動窗口;
比如他這段是[l,r]
, 也就意味著 如果我們固定左端點l
, 我們考慮 r
端點 是否具有單調性; [l, <r]
一定是合法的 且[l, >r]
一定是不合法的, 詳見@LINK: (https://editor.csdn.net/md/?not_checkout=1&articleId=135069315)-(@LOC_0)
;
即, 我們令Cost(l,r)
表示 (將這個區間的所有元素 變成相同的 的最小曼哈頓距離), 那麼對於ll>=l, rr<=r
則一定有Cost(ll,rr) <= Cost(l,r)
, 因為有這個性質 所以可以用滑動窗口;
接下來 你需要O(1)
的計算出 Cost(l,r)
的值, 這可以通過前綴和處理;
int maxFrequencyScore(vector<int>& A, long long K) {
sort( A.begin(), A.end());
int N = A.size();
PrefixSum_1D.Initialize( N); FOR_( i, 0, N-1){ PrefixSum_1D.__A[i] = A[i];} PrefixSum_1D.Work();
auto Cost = [&]( int _l, int _r){
auto mid = (_l+_r)>>1;
auto ANS = A[mid] * 1LL * (mid-_l+1) - PrefixSum_1D.Get_intervalSum(_l, mid);
if( mid+1 <= _r){
ANS += ( PrefixSum_1D.Get_intervalSum(mid+1, _r) - A[mid] * 1LL * (_r-mid));
}
return ANS;
};
int ANS = 1;
for( int r=0, l = 0; l < N; ++l){
while( r+1<N && Cost(l,r+1)<=K){
++r;
}
ANS = max( ANS, r - l + 1);
}
return ANS;
}