题解:CF1969D Shop Game
思路
考虑 Bob 的策略:
如果 Alice 选了物品 ,那么 Bob 如果选了物品 会使得 Alice 对于这个物品亏多少钱。
原来赚的钱:。
Bob 选后本金的亏损:。
那么 Bob 如果选了物品 会就使得 Alice 对于这个物品亏: 。
所以我们知道 Bob 应该选前 大 的 才能使在目前 Alice 选出的物品中让他亏损最多。
考虑 Alice 的策略:
首先 Alice 选出的前 大的物品一定不赚钱。
那么如果选的物品不多于 且至少选了一个物品,就一定是亏的,在最坏的情况下,我们可以什么都不选来保证不亏钱。
所以可以选 个物品来献祭,来让其它选的物品赚钱,但也不是选哪个献祭都可以,那 个献祭的物品的 必须在所有选出的物品的 里是前 大。
那么如何才能让献祭的物品由我们选择呢?可以选择将 由大到小排序,这样最左边的我们选择的 个物品就一定是献祭的,又由于我们需要献祭的钱最少,所以选 前 小的 就可以了。那么选的第 个物品往后怎么选?只需要挑 的选就行了,因为如果选其它的必然不赚钱。
但是我们不能只考虑前 个物品献祭,所以要枚举考虑献祭的最后一个 的分界线。
做法
将 排序,预处理 到 的买入价小于卖入价物品的利润,建立大根堆,存考虑到献祭的前 小的 ,维护堆内和。接下来枚举考虑献祭的最后一个 ,将 入堆,如果堆的大小大于 那么弹出堆顶。左边选前 小 的 物品,右边选 的 物品。用右边利润和减去堆内和来更新 的最大值。
注意 为 时要特判,因为后面的做法考虑的是 Bob 至少选一个物品。
一开始初值为 ,因为 Alice 可以什么都不选。
那么这道题就做完了,代码其实不长。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
bool cmp(pair<int,int> x,pair<int,int> y)
{
return x.first>y.first;
}
signed main()
{
int tt;
cin>>tt;
while(tt--)
{
int n,k;
cin>>n>>k;
vector<pair<int,int> > a(n+1);
vector<int> pre(n+2);
for(int i=1;i<=n;i++) cin>>a[i].second;
for(int i=1;i<=n;i++) cin>>a[i].first;
sort(a.begin()+1,a.end(),cmp);
for(int i=n;i>=1;i--) pre[i]=max(a[i].first-a[i].second,(int)0)+pre[i+1];
priority_queue<int> q;
int ans=0,sum=0;
if(k==0)
{
cout<<pre[1]<<endl;
continue;
}
for(int i=1;i<=n;i++)
{
q.push(a[i].second);
sum+=a[i].second;
if(q.size()>k&&q.size()) sum-=q.top(),q.pop();
if(q.size()==k) ans=max(ans,pre[i+1]-sum);
}
cout<<ans<<endl;
}
}

浙公网安备 33010602011771号