题目
给你长为n的数列a[],ai代表第i天的物价
第i天你可以选择三个操作其一:
①卖掉一个你已有的方块,收益为ai
②买一个方块,花费为ai
③什么都不做
n天后,问最大收益和最小操作次数
思路来源
https://www.cnblogs.com/chenquanwei/p/9536749.html
题解
只在卖的时候计算收益,买的时候不算损失
考虑一个单增序列,2 3 5 8 10
你可以买2 然后卖10
也可以买2 卖3买3 卖5买5 卖8买8 卖10
买的时候只把它加进优先队列里,不减收益
而卖的时候把它和买的时候的值作差
这样的话,二者的收益是一样的,只是次数的差别
然后注意到同一天的卖买是可以抵消的
那么如果这一天卖的值3是一个跳板,就给这个值3打上一个标记
下一次卖5的时候,发现买的3那个值是打过标记的,就减去这一次
相当于直接买2卖5
而除去这个卖的标记以外,当天也是一定可以买入的
如2 3 5 5的情形,买2 (卖3买3)买3 卖5 卖5
实际上等价于前两天买的东西再后两天卖
那么这就说明了,一个卖了的值,一定是可以立刻回买的,
最后一次卖的时候的买入在计算值时不会统计在答案内
而当天一定是可以买一次的,
这就说明一个值最多可以被买两次,
实质是买一次,当前面的跳板一次
心得
暑假里梁神讲过然而当时没能理解
现在终于算是能补上这道题了
还是思维不够见识不够叭
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int T,n,a[maxn],cnt;
ll ans;
map<int,int>sell;
priority_queue<int,vector<int>,greater<int> >q;
void init()
{
ans=cnt=0;
sell.clear();
while(!q.empty())q.pop();
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
init();
for(int i=0;i<n;++i)
{
scanf("%d",&a[i]);
if(!q.empty()&&q.top()<a[i])
{
int x=q.top();q.pop();
ans+=a[i]-x,cnt++;
q.push(a[i]);
//卖一次获利,卖了之后立刻买入,等以后可能的获利
sell[a[i]]++;
//但此刻卖过一次 之后再卖可以抵消这次的次数
if(sell[x])sell[x]--,cnt--;
//第一个值只能被买一次;但后面的值可以被买一次,还可以当一次跳板(实质是卖一次),故可以买两次
//这里恰好与q.empty()对应
}
q.push(a[i]);//每个值都可以被买一次
}
printf("%lld %d\n",ans,cnt*2);
}
return 0;
}