超市

【超市】

【问题描述】

超市里有N件商品,每个商品都有利润pi和过期时间di,每天只能卖一件商品,过期商品(即当天di<=0)不能再卖。求合理安排每天卖的商品的情况下,可以得到的最大收益是多少。

【输入格式】

输入包含多组测试用例,测试用例最多30组。
每组测试用例,以输入整数N开始,接下里输入N对pi和di,分别代表第i件商品的利润和过期时间。
在输入中,数据之间可以自由穿插任意个空格或空行,输入至文件结尾时终止输入,保证数据正确。

【输出格式】

对于每组产品,输出一个该组的最大收益值。每个结果占一行。
【样例输入】
4 50 2 10 1 20 2 30 1
7 3 1 2 1 10 3 100 2 8 2 1 20 50 10

【样例输出】

80
169

数据范围

0≤N≤10000,1≤pi,di≤10000

分析:

每天只能卖一件,过期不能卖。我们的任务是合理规划商品的卖出日期,使得收益最大。

贪心:按利润大小顺序,规划卖出时间,尽可能晚点卖,这样可能会给保质期短的商品留有更多选择机会。

先将商品按利润从大到小排序,然后按顺序规划卖出日期。卖出时间为,可以卖出的日期中的最晚的那一天。

设:排序后第i件商品的利润是 a[i].p,过期天数是a[i].d。

计算第I号商品可以最晚卖出的日期,从过期天数a[i].d 往前查,排除那些已经卖过商品的日期后的最晚的那一天。

直接模拟会超时。怎样更快找到最晚可卖日期?

方法一:并查集

不断往前找的过程,很像并查集中不断向上找祖宗的过程。

起初我们把所有日期抽象为一个集合,集合的祖宗(也可以说是集合的代表元素)是自己 。

我们把最晚售卖日期当成把每个集合的祖宗,同时安排个0天的集合,若是有某个商品查到的祖宗是0,表示它这个商品已经没法卖了。

并查集中“并”:

当某个商品的保质期是d,我们计算出的最晚售卖时间是x,那么我们就将x,x-1 所在集合合并。

含义是,下一次再有保质期是d的商品出现时,它的最晚售卖时间最大可能是x-1,当然x-1 那一天可能也卖过商品了,那它最晚售卖时间就是x-2了。

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 10005;
 4 int n, ans;
 5 int f[N];
 6 struct node {
 7     int t, val;
 8 } a[N];
 9 bool cmp(const node &x, const node &y) { return x.val > y.val; }
10 int find(int x) {
11     if (f[x] < 0)
12         return x;
13     return f[x] = find(f[x]);
14 }
15 int main() {
16     while (cin >> n) {
17         ans = 0;
18         memset(f, -1, sizeof(f));
19         for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].val, &a[i].t);
20         sort(a + 1, a + 1 + n, cmp);
21         for (int i = 1; i <= n; i++) {
22             int r1 = find(a[i].t);
23             if (r1 > 0)
24                 ans += a[i].val, f[r1] = r1 - 1;
25         }
26         printf("%d\n", ans);
27     }
28     return 0;
29 }

  

方法二:贪心

贪心:

用小根堆(收益最小的商品在堆顶)来维护打算卖出的商品集合。

按保质期从小到大的顺序扫描商品:

(1)放入小根堆,

(2)若刚加入堆的商品在保质期内无法卖出,那剔除堆顶收益最小的商品,给新入堆商品腾出售卖日期。

扫描结束后,堆中的商品就是能得到最大总收益的商品集合。

 

 

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int MAX = 100015;
 4 typedef long long LL;
 5 struct node {
 6     int p;
 7     int d;
 8 } a[MAX];
 9 
10 bool cmp(const node &x, const node &y) { return x.d < y.d; }
11 
12 int main() {
13     int n;
14     while (cin >> n) {
15         for (int i = 1; i <= n; i++) {
16             cin >> a[i].p >> a[i].d;
17         }
18 
19         priority_queue<node, vector<int>, greater<int> > heap;
20 
21         //按保质期从小到大的顺序处理每个商品
22         sort(a + 1, a + 1 + n, cmp);
23 
24         int ans = 0;
25         for (int i = 1; i <= n; i++) {
26             heap.push(a[i].p);
27             ans += a[i].p;
28 
29             //优先队列中保存了准备卖出的商品收益,价值最小的在顶。
30             //由于是按照保质期从小到大的顺序处理的
31             //所以堆中的商品保质期一定都是小于等于a[i].d的
32 
33             //堆中的每个商品占用一个售卖日期
34             //若其中的商品个数大于刚加入堆中的i号商品的保质期
35             //那么刚加入堆中的i号商品将无法在保质期前卖出,(因之前的商品占去了所有可卖的日期)
36             //所以需要就从堆中剔除一个商品,腾出一个售卖日期给新入商品
37             //那剔除哪一个商品呢?
38             //当然是剔除堆中收益最小的商品,也就是堆顶那个。
39             //当然剔除也可能是刚刚加入的那个i号商品,那一定是因为它的价值在堆中最小
40 
41             if (heap.size() > a[i].d) {
42                 ans -= heap.top();
43                 heap.pop();  // 时间d 前保证卖掉 d 件商品  每次去掉最小的
44             }
45         }
46 
47         cout << ans << endl;
48     }
49 
50     return 0;
51 }

 

 

 

 

posted @ 2023-07-27 18:10  flatten  阅读(32)  评论(0)    收藏  举报