此题就是遍历并不断维护当前区间的最优解,在比赛时想到了multiset,但在删除值时没处理好,使用值删除而不是迭代器指向删除,导致删除变复杂,后采用单调栈维护当前最优,但没处理明白,赛后参考了学长的代码,果然思路没错,一样是用multiset存储,维护当前区间求最优解。不过学长采用的迭代器指向删除的方式更加干净利落。
在更新区间时,需要注意,要先把旧的元素删掉并维持当前区间最优后才可加入新的区间,否则无法保证最优。
代码如下:
#include<bits/stdc++.h>
#include<set>
using namespace std;
#define ll long long
multiset<int> A,B; //A为被翻开的牌,B为没被翻的牌
ll sum=0,rsum=0; //sum为正面牌总和,rsum为翻面牌总和
ll ans=0;
ll n,k,l;
int a[100010],b[100010];
void maintain(){ //维护当前区间
while(A.size() >l){ //若翻开的牌数大于l
B.insert(*A.begin() ); //将翻开牌中最小的牌放入备选区
rsum-=*A.begin() ; //从总和减去
A.erase(A.begin() ); //删除
}
while(A.size() <l&&!B.empty() ){ //若翻开的牌数还不到l且备选区非空
auto p=--B.end() ; //p为指向备选区中最大牌位置的指针
rsum+=*p;
A.insert(*p); //将P放入A中
B.erase(p); //从备选区中删除
}
}
void erase(int i){ //删除操作
sum-=a[i]; //从正面牌总和中减去
if(A.find(b[i])!=A.end() ){ //如果被删去的牌是翻面的牌的话
rsum-=b[i]; //从翻面的牌中减去
A.erase(A.find(b[i]) ); //从翻面牌堆删除
maintain(); //维护当前区间
}
else B.erase(B.find(b[i]) ); //若不在,则从备选区中删去即可
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
cin>>k>>l;
for(int i=1;i<=k;i++){
sum+=a[i];
rsum+=b[i];
A.insert(b[i]);
}
maintain();
ans=max(ans,sum+rsum);
for(int i=1,j=n;i<=k;i++,j--){
erase(k-i+1); //减去旧的
sum+=a[j];
A.insert(b[j]);
rsum+=b[j]; //加上新的
maintain(); //维护一下
//该步骤有个注意事项:
//一定要先减去旧的再加上新的,顺序不可颠倒
//若先加上新的,再减去旧的,A的元素不变,无法成功更新成最大rsum
//先减去旧的话,会先从A中删一个掉,再从B中拿一个最大的过来
//此时再加上新的,元素个数溢出,就可以排掉A中最小的数值
//否则B中最大值有可能会大于A中最小值
ans=max(ans,sum+rsum);
}
cout<<ans;
return 0;
}