hdu6438 Buy and Resell(思维题/反悔贪心)

题目

给你长为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;
} 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Code92007

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值