CF865D Buy Low Sell High 题解
题解索引
- 题目大意
- Solution
- AC code
- 类似题型
代码类型: C++(cpp)
是否吸氧:否
不压行代码长度:25或25(两种实现方式)
题目大意
题面: <传送门>
题意:给出
您在第 无所事事。初始可以认为您有无限多的金钱,求出您可能获得的最大收益。
术语理解:反悔贪心
Solution
如果我们花费
于是我们就用“反悔贪心”的方式来解决。为啥叫“反悔贪心”?因为这是我们老师起的因为它在遇到一个更大收益时,优先选择更大的,即反悔次的,选择优的。
首先我们要买入。因为卖出需要买入才行……当栈空时,我们一定要买入(一会我会解答“不操作比买入更赚”的情况)。还有,我们在遇到相对便宜的价格时,也选择买入(这个挺麻烦的,讲到卖就懂了)。
卖出:首先我们要确定,我们遇到了一个价格比买入的物品更贵的,才能卖出,这样才能正收入。还有,我们认为“只有把这个股票卖出时,这个股票才算被买入过”——因为我们反悔的过程中可能更改很多次,所以我们这样认为。然后,我们当遇到一个更大的价格,我们优先选择反悔。如果遇到更小的,我们选择买入。
怎么实现?
维护一个集合,我们称为“目前的操作集合”。
- 第一次买入
直接加入集合即可。
- 后面的买入
比集合小就入集合(因为卖出才算是买入了,所以无所谓)。
- 卖出
我们将卖出为
为啥找不到那个值?因为我们每次遇到一个值,是优先跟集合里的最小值比较的,毕竟能挣一点是一点,优先跟最小的比较是最优的。
具体代码流程?
(这里为了方便用 priority_queue 来展示,其实我第一时间想到的是 multiset)
n=read();
while(n--){
ll x=read();
if(!q.empty()&&q.top()<x){//栈空一定插入,或者今天的价格比集合内最小值大
ans+=x-q.top();//赚差价(
//我们可以认为卖出时也是一种买入,于是就有了这玩意
q.pop();//要提出队头,因为被反悔了
q.push(x);//反悔
}q.push(x);
}
AC code
方法一:multiset 可重集合,自动排序(默认升序)。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<set>
using namespace std;
typedef long long ll;
inline ll read(){
register int f=1,x=0;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}multiset<ll> s;
ll n,ans=0;
int main(){
n=read();
while(n--){
ll x=read();
if(!s.empty()&&*s.begin()<x){
ans+=x-*s.begin();
s.erase(s.begin());
s.insert(x);
}s.insert(x);
}printf("%lld\n",ans);
return 0;
}
方法二:priority_queue 优先队列,可惜默认大根堆(这也是为啥我优先选择 multiset 的原因)。
#include<iostream>
#include<cmath>
#include<cstdio>
#include<queue>
using namespace std;
typedef long long ll;
inline ll read(){
register int f=1,x=0;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}priority_queue<ll,vector<ll>,greater<ll> >q;
ll n,ans=0;
int main(){
n=read();
while(n--){
ll x=read();
if(!q.empty()&&q.top()<x){
ans+=x-q.top();
q.pop();
q.push(x);
}q.push(x);
}printf("%lld\n",ans);
return 0;
}
AC 记录1 <传送门>
AC 记录2 <传送门>
类似题型
忘了还有哪道是反悔贪心了(