HDU - 6438(贪心+思维)

链接:HDU - 6438

题意:给出 n ,表示 n 天。给出 n 个数,a[i] 表示第 i 天,物品的价格是多少。每天可以选择买一个物品,或者卖一个已有物品,也可以什么都不做,问最后最大能赚多少钱,最少操作次数是多少?

题解:依次遍历每一天,比如当前是第 i 天,用堆维护前 i - 1 天的最小值,如果最小值比 a[i] 大,直接将 a[i] 加入堆里就可以了,如果日后遇到比 a[i] 大的再更新答案。如果最小值比 a[i] 小,那么就将其卖出,更新答案: 赚的钱 ans += a[i] - min, 操作次数 cnt += 2,但是这样并不能保证答案一定最优的,因为后面可能遇到收购价格更高的那一天,采取的方法是将 min 替换成 a[i],并记录 a[i] 是已经交换过的数,如果日后遇到比 a[i] 大的数 a[j],即遇到更优答案了,就再卖一次 a[i],更新答案,但是注意此时钱更新是没有问题的,但是操作次数就不能累加了,这样相当于min买,a[j]卖。替换后,堆还要再加一次 a[i],因为最优答案里第 i 天也可能是买物品的。相当于pop一个min,push两个a[i]。

#include <bits/stdc++.h>
using namespace std;

const double EPS = 1e-6;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const int maxn = 1e5 + 10;
int n;
int a[maxn];
priority_queue<int, vector<int>, greater<int> > Q;
map<int, int> mp;

int main()
{
    int T;
    scanf("%d", &T);
    while(T--){
        scanf("%d", &n);
        for(int i = 0; i < n; i++) scanf("%d", &a[i]);

        while(!Q.empty()) Q.pop(); mp.clear();

        long long ans = 0, cnt = 0;
        for(int i = 0; i < n; i++){
            if(!Q.empty() && Q.top() < a[i]){
                int x = Q.top(); Q.pop();
                ans += a[i] - x; cnt++;

                if(mp[x]){ mp[x]--; cnt--;}

                Q.push(a[i]); mp[a[i]]++;
            }
            Q.push(a[i]);
        }

        printf("%lld %lld\n", ans, cnt << 1);
    }

    return 0;
}

 

posted @ 2018-08-26 11:40  鬼沐冢  阅读(686)  评论(0编辑  收藏  举报