这题看起很简单,但是想法不好,代码就会又臭又长,想法好代码就很香了(x)
题意:依次经过n个城市,每个城市有物价ai,在每个城市只能买一、卖一或者什么都不做,求在买卖商品的最大毛利和最少交易次数(买卖都算一次交易),假设金钱无限。
思路:
1.首先假设手头有一些商品,来到一个物价更高的城市,为了最大毛利,肯定会把最便宜的i在此城市以aj的价格卖掉,获利aj-ai,交易次数是i买j卖。每次都是对最便宜的操作。
2.如果下一次来到了城市k,且ak>aj(也就是先前卖掉i物品的地方),那我们本应该把i在k卖掉更优,现在怎么办呢?其实这不影响我们计算答案,因为只要收益加上ak-aj即可,因为容易想到,ak-ai=(ak-aj)+(aj-ai)。那么交易次数呢?原先i~j的交易是两次,如果i~k交易那也是两次,也就是说“反悔”是不增加交易次数的。
3.根据2的发现,可知计算答案与具体哪个物品在哪交易没有直接关联,我们只需要知道曾经卖出的城市,是否被反悔。如果被反悔的话,为了最大化收益,我们都假设不卖就买(但不计数)。如果没有反悔,每次卖出,cnt+2,ans+差值。如果时因为反悔而选择的新的卖出地,cnt不变,ans+与上个卖出地的差值。
4.根据上面的分析,我们需要一个数据结构,遍历所有的地区,每次拿出最便宜的“商品”或者“卖出地”进行比较,
如果当前地区物价不高于拿出的“商品”或者“卖出地”,那么没有收益,“商品”的形式存入结构。
如果高于,那么可以有收益,取出这个“商品”或者“卖出地”,
ans都加上差值,
若是商品则cnt+2,
若是卖出地则cnt不变,但是需要存入上个出卖地的商品,体现不卖则买。
把当前卖出地存入结构。
5.讲到这里完全可以用结构体+优先队列小顶堆来做啦!结构体需要重载一下小于号,存入v和id,分别表示这个“商品”或者“卖出地”的价值,和上次出卖的地方(没有就记为inf,优先更新卖出地,使得交易次数最少)。稍微处理一下应该很好做。下面这份代码给出了更加“偷懒”的写法,用pair打标记,first是价值,second标签1是出卖地,标签2是商品,每次交易都把出卖地和商品都压入,由于排序的原因,出卖地总会在前面,那么如果出卖地被更新了,商品就会在排序中浮上来了。消耗空间多一点,但是非常轻松。
(typedef似乎快一些?)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pll;
int main( )
{
int T,n;
scanf("%d",&T);
while(T--)
{
ll ans=0,cnt=0;
priority_queue<pll,vector<pll>,greater<pll> >q;
scanf("%d",&n);
for(int v,i=1;i<=n;i++)
{
scanf("%d",&v);
if(!q.empty()&&q.top().first<v){
cnt++;
pll t=q.top();q.pop();
if(t.second==1)cnt--;
else cnt++;
ans+=v-t.first;
q.push({v,1});
q.push({v,2});
}else{
q.push({v,2});
}
}
printf("%lld %lld\n",ans,cnt);
}
return 0;
}
/*
5
5
1 2 3 4 5
5
2 2 9 9 2
*/