超市购物 / Supermarket
题目链接:ybt高效进阶3-1-5 / POJ 1456
题目大意
有一些商品,每个有价值和最晚卖出时间。
一天只能选一个卖出。
问你最大价值。
思路
这道题我们考虑贪心。
你想,一个东西它最晚卖出时间是 x x x,那它就只能在 1 ∼ x 1\sim x 1∼x 的时间点的其中一个卖出。
那我们发现我们把时间
x
x
x 往前移的时候,在时间
x
x
x 之后能卖的东西就越来越多。
那我们想想,在时间往前移一个单位的时候,在新出现的时间卖什么最好呢?
不是这个时间结束的里面价值最大的,而是在这个时间之后能买的东西中没有被选过的东西中价值最大的。
那怎么实现呢?
我们考虑用堆。
先按最晚卖出时间从后往前排序,如果一样就按价值从大到小排。
然后枚举每个物品,把它放进堆里面。然后它比前面一个东西的最晚卖出时间早了,早了多少就把堆上面的多少个东西卖出。(其实意思就是在中间空的每个时间点都选一个最优的东西卖出)
当然,如果堆里面没东西,就是能卖的都卖完了,就可以直接不卖了。
有一点就是记得把 1 ∼ x 1\sim x 1∼x 这段时间也要卖东西( x x x 是最早的最晚卖出时间),因为这段时间也可以卖东西,而且什么都可以卖。
不过这道题好像有并查集的做法。
我好像之间还用并查集的做法做过。(用堆写完才发现的)
如果想看并查集的做法可以点击下面的链接跳转:
https://blog.csdn.net/weixin_43346722/article/details/90299744
代码
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node {
int x, t;
}a[10001];
int n;
long long ans;
priority_queue <int, vector<int>, less<int> > q;
bool cmp(node x, node y) {
if (x.t != y.t) return x.t > y.t;
return x.x > y.x;
}
int main() {
while (scanf("%d", &n) != EOF) {
while (!q.empty()) q.pop();
memset(a, 0, sizeof(a));
for (int i = 1; i <= n; i++)
scanf("%d %d", &a[i].x, &a[i].t);
sort(a + 1, a + n + 1, cmp);
//按卖出时间从后往前排序,如果卖出时间相同,那就把价值大的放在前面
ans = 1ll * a[1].x;
for (int i = 2; i <= n; i++) {
q.push(a[i].x);
if (a[i].t != a[i - 1].t)//有时间给你选
for (int j = 1; j <= a[i - 1].t - a[i].t; j++) {
if (q.empty()) break;
ans += 1ll * q.top();//不停的选可以选的价值最大的
q.pop();
}
}
for (int i = a[n].t - 1; i >= 1; i--) {//记得统计 1 时间到最早结束时间卖的东西的价值
if (q.empty()) break;
ans += 1ll * q.top();
q.pop();
}
printf("%lld\n", ans);
}
return 0;
}