算法竞赛进阶指南-59.超市

题目链接

算法竞赛进阶指南-59.超市

超市里有 NN 件商品,每件商品都有利润 pip_i 和过期时间 did_i,每天只能卖一件商品,过期商品不能再卖。

求合理安排每天卖的商品的情况下,可以得到的最大收益是多少。

输入格式

输入包含多组测试用例。

每组测试用例,以输入整数 NN 开始,接下来输入 NNpip_idid_i,分别代表第 ii 件商品的利润和过期时间。

在输入中,数据之间可以自由穿插任意个空格或空行,输入至文件结尾时终止输入,保证数据正确。

输出格式

对于每组产品,输出一个该组的最大收益值。

每个结果占一行。

数据范围

0N100000 \le N \le 10000,
1pi,di100001 \le p_i,d_i \le 10000
最多有 1414 组测试样例

输入样例:

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

Method : 贪心 + 优先队列

贪心策略:对于每个时间t,我们希望对于当前所有未过期的商品,在t天内尽可能选取利润前t大的商品

具体做法就是,首先按照过期时间来排序,然后按该次序从前往后遍历商品,并以商品价值建立一个小根堆:

把当前商品插入到堆中,如果当前商品的过期时间>堆中的商品数,因为之前所有的商品的过期时间都<=该商品的过期时间,所以如果以当前堆中的商品数为售卖天数t,必然不能把这些商品都卖光,因此需要弹出价值最小的商品(堆顶)。

这道题虽然思维难度不高,但是让我懂得了结构体的优先队列该怎么写最优美(重载operator >)。

#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e4 + 7;

struct Good {
    int value;
    int time;
    bool operator >(const Good& rhs) const{
        return value > rhs.value;
    }
};

Good g[N];

int main() {
    int n;
    while (cin >> n) {
        for (int i = 0; i < n; i ++) {
            scanf("%d %d", &g[i].value, &g[i].time);
        }
        sort(g, g + n, [](const Good& a, const Good& b) {
            if (a.time == b.time) return a.value < b.value;
            return a.time < b.time;
        });
        
        priority_queue<Good, vector<Good>, greater<Good>> pq;
        for (int i = 0; i < n; i ++) {
            pq.push(g[i]);
            if (pq.size() > g[i].time) pq.pop();
        }
        
        int res = 0;
        while (!pq.empty()) {
            res += pq.top().value; pq.pop();
        }
        printf("%d\n", res);
    }
    
    return 0;
}

复杂度分析

  • 时间复杂度:O(2nlogn){O(2*n\log n)},sort是O(nlogn)O(n\log n)的,n次循环*pq的push() or pop也是O(nlogn)O(n\log n)

  • 空间复杂度:O(N){O(N)}