Buy and Resell

题目链接: Buy and Resell

大致题意:

有n个城市, 在每个城市中你可以选择花费a[i]的价格买一个能量块, 也可以选择以a[i]的价格卖出一个能量块(前提是你要有能量块可以卖). 你会从1号城市依次走到n号城市, 每次可以选择买或者卖或者什么也不做. 在初始你有无限多的钱的前提下, 问你能获得的最大利润, 以及在最大利润的前提下最小的买卖次数.

解题思路:

首先如果一件物品买了后再卖了能赚钱, 则我们一定会采取这种方案, 因为题目要求最大利润优先, 最小的买卖次数则是不做无意义的买卖, 如花费x元购买能量块后又以x元卖出.

贪心思路:

因为我们难以决定在第i个城市我们采取怎么样的操作是最优的, 所以不妨对于每一个城市, 我们都认为我们可以从该城市买入能量块, 并且在j城市(j>i)卖出(但是在卖出之前, 我们认为我们在i城市什么也没做). 而j城市的能量块价格是一定为a[j]的, 所以此时我们如果要卖出能量块, 一定要以最小的价格在i城市买入能量块. 对于买入能量块的城市i, 我们可以采用小顶堆来维护, 而城市j的确定则是难点.

假设我们现在存在一个小顶堆, 里面存放的元素代表可以用x的价格买入能量块(即所有的i城市), 当我们到达j城市, 此时如果满足a[j] > heap.top(), 我们此时选择卖出一定可以赚到钱, 且为一种优质策略.
此时我们的利润变化为: res = res + a[j] - x. (记为*式)

但是我们无法保证后续不会存在城市k (k>j), 满足a[k] > a[j], 则我们发现不如在k城市卖出刚才的能量块, 所以我们此时就要有一种反悔策略, 即: 刚才的能量块不选择在j城市卖出(即我们又可以以a[j]的价格买入能量块, 应当把a[j]入堆), 而在k城市卖出该能量块. 本身我们花费了x元买入, 卖出得到了a[j]元, 变成了x元买入, a[k]元卖出. 则利润变化为: res = res - a[j] + a[k]. 做一个变形, 类比于*式, 我们得到 res = res + a[k] - a[j].
从操作次数的角度看, 我们在j城市买入能量块, 在k城市卖出, 结合之前i城市的操作, 相当于我们在j城市卖了一次, 又买了一次. 所以在j城市的操作并不是合法操作, 不应计算在结果中.

而从利润的角度看, 我们获得的利润可以看作买了i, 在j卖出, 又在j买入, 在k卖出. 我们发现这两种情况的利润遵循相同的公式.

AC代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int t; scanf("%d", &t);
    while (t--) {
        int n; scanf("%d",&n);

        priority_queue<int, vector<int>, greater<int>> q; //小顶堆
        map<int, int> mp; //first:价格, second:反悔次数 (也可以以城市编号来标记是否可以反悔)

        ll res = 0, cou = 0; //res表示利润, cou表示操作次数
        for (int i = 1; i <= n; ++i) {
            int x; scanf("%d", &x);

            if (!q.empty() && q.top() < x) { //说明应当卖出堆顶元素
                int sell = q.top(); q.pop();
                res += x - sell; //利润变化
                if (mp.count(sell)) { //看看当前城市属于j城市还是k城市
                    //属于k城市
                    if (--mp[sell] == 0) mp.erase(sell); //已经卖出过了, 需要反悔
                    q.push(sell); //j城市的商品我们仍可以购买
                }
                else cou++; //买卖次数++
                mp[x]++; //既然在当前城市卖出了能量块, 那么对于价格x, 我们就可以反悔.
            }
            q.push(x);
        }
        printf("%lld %lld\n", res, cou * 2);
    }
    return 0;
}

END

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

逍遥Fau

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

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

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

打赏作者

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

抵扣说明:

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

余额充值