题意:每到一个位置可以买一个东西或卖一个东西或什么也不做;问最大利润及最大利润情况下使用的最短时间;
思路:贪心,用一个优先队列结构去存之前准备买的东西(但还没买),遇到比堆中最小值大的值时,就把堆顶元素pop掉(这里开始才算完成交易),同时把当前值入队两次,为什么是两次?
由于当前值的选择不一定是最优的选择,后面可能有更好的选择,故第一次是用来作为中间位置存入以便进行进一步更大利润的交易。
第二次是将当前值作为带买商品存入堆,可能之后会交易到(但交易到之前作为中间位置的值也已经被交易)
由于题目还问了所需的最短时间,故用一个map来存每个元素当作中间元素的总次数,当总次数不为0是,该元素出队不加时间(因为它只是中间元素)。
代码如下:(注意优先队列变成小根堆要用G++提交,不然会编译错误…)
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
typedef long long ll;
//贪心加优先队列:买低卖高问题;
int main()
{
int i,j;
int t;
int n;
ll now;
ll ans;
int cnt;
scanf("%d",&t);
while(t--)
{
priority_queue< ll,vector<ll>,greater<ll> >q;
map<ll,int>mp;//用来记录当前元素作为中间买卖位置的次数
mp.clear();//避免容器中有未初始化的空间
while(!q.empty())
{
q.pop();
}
ans=0;
cnt=0;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%lld",&now);
if(!q.empty()&&now>q.top())
{
int tp=q.top();
ans+=now-tp;
cnt++;
q.pop();
q.push(now);
q.push(now);
if(mp[tp]!=0)//即堆顶元素是作为其他元素买卖的中间位置,并不是真正卖掉
{
cnt--;
mp[tp]--;
}
mp[now]++;//now暂时作为中间位置,mp++;
}
else
{
q.push(now);
}
}
printf("%lld %d\n",ans,cnt<<1);//cnt要乘2因为买卖都要算一次;
}
return 0;
}