贪心+二叉堆or贪心+并查集

Supermarket

http://www.nowcoder.com/questionTerminal/a24e7e596ffd4936a0d5c6f2312579be

题号:NC50995,蓝书上面的题目,难度不大,简单思维+基本算法(堆或者并查集)
传送搓我

推荐理由and知识点:简单贪心攻略比较锻炼到我,再结合可以多方面思考解题方法,从堆和并查集两方面(基本的算法结构)都可以解题。

中文题目大意:

多组输入、给定N个商品,每个商品有利润pi和过期时间di,每天只能卖出一个商品,过期商品不能再卖,如何安排每天卖的商品,可以使得利益最大。1图片说明 N,pi,di图片说明 1e4。

极其不推荐做法:直接暴力贪心从利润大到小排序,从最大利润商品开始;用一个j初始化为该商品过期时间,直到大于等于0,找到一天空闲卖出,累加利润,break;时间复杂度O(n^2) ,题目比较水,建议卡掉这种办法,可以通过下面并查集优化得到正确解法。

解题思路1(二叉堆优化):

容易想到一个贪心策略:在最优解里面,每一天t中,应该在保证卖出商品都是没有过期得前提下,尽可能卖出利润前t大得商品。因此,我们可以依次考虑每个商品,动态的维护一个满足上面性质得方案。
具体到代码中就是,按照商品过期天数递增排序,建立一个小根堆,因为STL中存在优先队列直接使用就行了。我们使得小根堆中得元素保存为我们每一天需要卖出的商品利润,最终求和。
从1开始遍历N件商品,因为商品以及按过期时间排序,所以分为下面2种情况。
1、如果该商品的过期时间,大于现在堆中元素个数,说明一定可以找个时间把它卖出去,直接插入堆中。
2、否则,就是当前这件商品过期时间t内,安排了t件商品出售,我们需要拿到堆顶元素(收益最小),与这件商品收益对比,如果这件商品收益大,那我们就选择更换商品在这一天出售。(或者说当存在多件商品过期天数相同的情况下,我们看是不是要在前t天中少买一件别的商品,把它卖出去)

该算法时间复杂度O(N * log N)

Code1

#include 
using namespace std;
struct Node {
    int p, d;
}a[10005];
bool cmp1(Node a, Node b) {
    return a.d < b.d;
}
priority_queue, greater > pq; //存储我们卖的物品价值
int main() {
    int n;
    while (~scanf("%d", &n)) {
        for (int i = 1; i <= n; ++i) {
            scanf("%d %d", &a[i].p, &a[i].d);
        }
        // 按照过期时间升序排序
        stable_sort(a + 1, a + 1 + n, cmp1);
        for (int i = 1; i <= n; ++i) {
            int t = a[i].d;
            if (t > pq.size())   pq.push(a[i].p); // 情况1
            else { //情况2(过期时间与前一个商品相同时)
                int pp = pq.top();
                if (pp < a[i].p) { //比较是否更换,如果收益更大选择替换
                    pq.pop();
                    pq.push(a[i].p);
                }
            }
        }
        int ans = 0;
        while (pq.size()) { //遍历优先队列全部元素,累加求和就是答案
            ans += pq.top();
            pq.pop();
        }
        printf("%d\n", ans);
    }
    return 0;
}

解题思路2(并查集优化):

上面我们通过二叉堆,有了一种解题方法,还有另一种优化方法,就是使用并查集,想想为什么我们最开始暴力贪心不行因为第二重枚举用时太大,可以通过一个并查集数组来减少我们的时间开销,达到优化目的。
1、首先我们按照商品的收益降序排序,收益越大越早处理,并且根据贪心思想,我们要保证“决策包容性”所以我们在选择找个商品出售时间都应该选择尽可能晚的卖出去。
2、这样建立一个关于“天数”的并查集f,初始化为-1,我们用x表示该商品过期时间天数,Find(x),如果f[x]==-1;说明这一天没有商品被卖出去,直接让该商品在这一天卖出去,用一个变量t接受卖出去的天数;并且合并f[t]=t-1,并且累加利润;即下一次访问这个天数,根据并查集特性访问前一天是否合法。直到Find()结束。如果Find返回的不是0;就说明找到了一天空闲,可以安排一天把该商品出售,累加利润,f[t]=t-1;否则如果t==0,说明在该商品过期天数内,存在也会过期,并且利润比你大的商品,只能放弃该商品不卖,继续遍历下一个商品。直到全部商品遍历完毕,最终利润和求解完毕

该算法用时应该比二叉堆快一点

Code2:

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
typedef long long ll;

inline int read() {
    int s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}

const int N = 1e4;
struct Node {
    int p, d;
    bool operator <(const Node& a)const {
        return this->p > a.p;
    }
}p[N + 5];
int f[N + 5];

int find(int x) {
    if (f[x] == -1) return x;
    return f[x] = find(f[x]);
}

int main() {
    int n;
    while (~scanf("%d", &n)) {
        memset(f, -1, sizeof(f));
        for (int i = 1; i <= n; ++i) {
            p[i].p = read();
            p[i].d = read();
        }
        stable_sort(p + 1, p + 1 + n); //价值降序排序
        int ans = 0;
        for (int i = 1; i <= n; ++i) {
            int t = find(p[i].d); //返回可以出售的最小时间
            if (t > 0) { //如果找到有一天可以出售该商品
                ans += p[i].p; //累加利润
                f[t] = t - 1; //合并t和t-1
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
全部评论
爱了爱了
点赞 回复 分享
发布于 2020-04-09 16:19

相关推荐

专心打鱼:互联网搬运工,贴子都要偷
点赞 评论 收藏
分享
在评审的大师兄很完美:像这种一般就是部门不匹配 转移至其他部门然后挂掉 我就是这样被挂了
点赞 评论 收藏
分享
最近又搬回宿舍了,在工位坐不住,写一写秋招起伏不断的心态变化,也算对自己心态的一些思考表演式学习从开始为实习准备的时候就特别焦虑,楼主一开始选择的是cpp后端,但是24届这个方向已经炸了,同时自己又因为本科非92且非科班,所以感到机会更加迷茫。在某天晚上用java写出hello&nbsp;world并失眠一整晚后选择老本行干嵌入式。理想是美好的,现实情况是每天忙但又没有实质性进展,总是在配环境,调工具,顺带还要推科研。而这时候才发现自己一直在表演式学习,徘徊在设想如何展开工作的循环里,导致没有实质性进展。现在看来当时如果把精力专注在动手写而不是两只手端着看教程,基本功或许不会那么差。实习的焦虑5月,楼主...
耶比:哲学上有一个问题,玛丽的房间:玛丽知道眼睛识别色彩的原理知道各种颜色,但是她生活在黑白的房间里,直到有一天玛丽的房门打开了她亲眼看到了颜色,才知道什么是色彩。我现在最大可能的减少对非工作事情的思考,如果有一件事困扰了我, 能解决的我就直接做(去哪里或者和谁吵架等等……),解决不了的我就不想了,每一天都是最年轻的一天,珍惜今天吧
投递比亚迪等公司10个岗位 > 秋招被确诊为…… 牛客创作赏金赛
点赞 评论 收藏
分享
评论
2
收藏
分享
牛客网
牛客企业服务