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; }