AcWing 145. 超市
题目描述
超市里有 N 件商品,每件商品都有利润 pi 和过期时间 di,每天只能卖一件商品,过期商品不能再卖。
求合理安排每天卖的商品的情况下,可以得到的最大收益是多少。
输入输出
多实例问题
输入一个整数N,然后输入N对的 pi 和 di ,分别代表第i件商品的利润和过期时间。
数据范围
0 < N < 10000
1 <= pi, di <= 10000
输入样例
4 50 2 10 1 20 2 30 1
7 20 1 2 1 10 3 100 2 8 2
5 20 50 10
输出样例
80
185
思路
这道题很明显是一道贪心问题,因此我们容易想到一个贪心策略:在最优解中,对于每个时间t,应该在保证不卖出过期商品的前提下,尽量卖出利润前 t 大的商品。因此,我们可以依次考虑每个商品,然后动态维护一个满足上述性质的方案。根据以上的描述,我们应该就能想到这道题的大概思路了,即按照贪心策略每次选取保质期合法下利润最大的商品。
详细的说,我们把商品按照过期时间排序,建立一个初始为空的小根堆(节点权值为商品利润),然后扫描每个商品。具体做法如下:
1、若当前商品的过期时间t等于当前堆中的商品个数,则说明在目前方案下,前 t 天已经安排了 t 个商品卖出。此时,若当前商品的利润大于堆顶权值(即已经安排的t个商品中的最低利润),则替换掉堆顶,因为当前商品优于堆顶的商品。
2、若当前商品的过期时间大于当前堆中的商品个数,直接把该商品放入堆中即可。
最终,堆中的商品即为我们要卖出的商品,利润之和即为答案。
本题采用小根堆结构来动态储存商品数据的原因是因为:该题在遍历的过程中需要不断与已选择商品中的最小值进行比较与替换,然后小根堆结构可以实现堆中最小值的返回和删除。
时间复杂度:O(N logN) .
C++实现
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define IOS ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
#define ll long long
#define endl '\n'
typedef pair<int, int> pir;
const int mod = 0x7f7f7f7f;
const int N = 10015;
pir x[N];
priority_queue<int> heap; // 默认大根堆, 取负值则为小根堆
bool cmp(pir n1, pir n2){
if(n1.second == n2.second) return n1.first < n2.first;
return n1.second < n2.second;
}
int main(void){
IOS
int n;
while(cin >> n){
for(int i = 1; i <= n; i ++ ){
cin >> x[i].first >> x[i].second;
}
sort(x + 1, x + 1 + n, cmp);
for(int i = 1; i <= n; i ++ ){
if(x[i].second <= (int)heap.size() && -heap.top() < x[i].first){
heap.pop();
heap.push(-x[i].first);
continue;
}
if(x[i].second > (int)heap.size()){
heap.push(-x[i].first);
}
}
int ans = 0;
while(heap.size()){
ans += heap.top();
heap.pop();
}
cout << -ans << endl;
}
return 0;
}