N. Numbers on both Sides

此题就是遍历并不断维护当前区间的最优解,在比赛时想到了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;
}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值