HDU 6438 Buy and Resell(思维,贪心,最小堆)

HDU 6438 Buy and Resell

题目

给 n 个城市物品价格,每个地方的物品价格不一样。低买高卖就可以赚差价。
从1 ~ n,每经过一个城市可以选择以当前城市价格买进一个商品,也可以以当前城市价格卖掉手中的一个商品。还可以选择什么都不干直接走。问最后能赚的最大利润是多少。

分析

贪心,不过思路很巧妙。

ans 代表最后利润,cnt 代表操作数,这里标记买进操作,最后输出 2 * cnt 即可。

首先用优先队列存我们已经买的物品价格,对于当前城市,如果队列中的最小值,也就是已经买的最便宜的值,小于当前物品价格,这样就造成了一个最大差值。这时我们完成了一笔交易。我们在结果加上。

但是有一个问题,这样得到的不是最优解。增加一个标记变量,标记每一笔交易中卖出的价格。完成这笔 A 交易后,将买进价格 x 出队,将卖出价格 y 入队,这样再后面的交易中,如果有城市价格跟 A 交易卖出价格 y 的差值最大。那么我们可以将这一笔交易反悔,以当前城市价格卖出。

相应的,A 交易卖出价格 y 等于成为了中转商品,为了使交易次数尽可能小,可以当作中转商品没有参加交易。这是标记变量就起了作用。

具体操作就是每完成一笔交易,判断交易的买进价格是否被标记过。(即是不是已经是一笔交易的卖出价格),如果是,那么标记清空,交易次数减一(cnt–;)。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
#define INF 0x3f3f3f3f
#define fuck(x) cout<<x<<endl
const int N = 1e5 + 5;
const ll mod = 1e9 + 7;

int t, n;
ll ans, cnt;

int main(){
    scanf("%d", &t);
    while(t--){
        scanf("%d", &n);
        ans = cnt = 0;
        map<int, int> vis;      // 买卖一次 为 交易一次
        priority_queue<int, vector<int>, greater<int>> q;
        for(int i = 0, x; i < n; i++){
            scanf("%d", &x);
            q.push(x);           // 假买
            if(q.top() < x){     // 有值小于当前值,真卖 x  
                ans += x - q.top();         // 答案加上 利润
                cnt++;                      // 交易数
                if(vis[q.top()]){           // 如果 top 是前面某次交易的卖价
                    vis[q.top()]--;         // 那么 top 就当作了中转
                    cnt--;                  // top 清除标记代表之后还可以交易
                }
                vis[x]++;           // 标记 x 为交易的卖价
                q.pop();            //  
                q.push(x);          // 假如后面 x 被当作了中转,
                // 那么添加两次 x ,是为了让后面还有 x 还有机会被当作交易的买价。
            }
        }
        printf("%lld %lld\n", ans, cnt << 1);
    }
    return 0;
}

/*


*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值