Supermarket —— (优先队列 || 并查集优化)

Supermarket

method 1:优先队列(47ms)

毕竟看起来就像优先队列的题目,第一反映就是沿着时间线反向看,将所有的截止日期大于等于第 i 天的商品全都push进优先队列,然后将堆顶(价值最大的)弹出,就是在这一天要出售的商品。下面是AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 10001
using namespace std;

struct products{
    int val, ddl;
    bool operator < (const products& a) const // 在优先队列里的排序依据是价值大小
    {
        return val < a.val;
    }
};
int n;

bool cmp(const products& a, const products& b) // 注意加const关键字(之前没有加,被判成ce)
{
    return a.ddl > b.ddl;
}

int main()
{
    while(~scanf("%d", &n))
    {
        products pro[N]; // 开始把它定义成全局数组了,不知道是哪里没有清零的问题答案不对,这样就没问题了
        int res = 0;
        priority_queue<products> que;
        int ed = -1;

        for (int i = 0; i < n; i ++)
        {
            scanf("%d%d", &pro[i].val, &pro[i].ddl);
            ed = max(ed, pro[i].ddl); // 找到最大的截止日期
        }
        sort(pro, pro + n, cmp); // 先sort一遍可以简省下面一片代码的id(商品的序号)和ava(是否待售available)的空间

        int last = 0;
        for (int i = ed; i >= 1; i --) // 从最大的截止日期开始往回看
        {
            for (int j = last; j < n && pro[j].ddl >= i; j ++) // 将所有的满足ddl >= i的商品全都push进去
            {
                que.push(pro[j]);
                last = j + 1; // 用一个last来标记上次取到了第几个,下次从第几个开始push(防止重复push)
            }

            if(que.size())
            {
                products t = que.top();
                que.pop();
                res += t.val;
            }
        }
        printf("%d\n", res);
    }
    return 0;
}

下面是两个mle的代码,每件商品的结构体还额外定义了id(序号)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 10001
using namespace std;

struct products{
    int val, ddl;
    int id; // 没有sort,每次用id查询原来的位置,在销售之后,把该商品的val标记成-1
    bool operator < (const products& a) const
    {
        return val < a.val;
    }
};
int n;

int main()
{
    while(~scanf("%d", &n))
    {
        products pro[N];
        int res = 0;
        priority_queue<products> que;
        int ed = -1;

        for (int i = 0; i < n; i ++)
        {
            scanf("%d%d", &pro[i].val, &pro[i].ddl);
            ed = max(ed, pro[i].ddl);
            pro[i].id = i;
        }

        for (int i = ed; i >= 1; i --)
        {
            for (int j = 0; j < n; j ++) //可以发现不预先sort一遍,连循环的时间复杂度都要高些
            {
                if(pro[j].ddl >= i && pro[j].val != -1) //找到一件满足时间要求并且待售的商品
                {
                    que.push(pro[j]);
                }
            }

            if(que.size())
            {
                products t = que.top();
                que.pop();
                res += t.val;
                pro[t.id].val = -1;
            }
        }
        printf("%d\n", res);
    }
    return 0;
}

下面这个是最原始的,还多开了一个ava布尔数组(ava = true 表示待售)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define N 10010
using namespace std;

struct products{
    int val, ddl;
    bool ava;
    int id;
    bool operator < (const products& a) const
    {
        return val < a.val;
    }
    products(): ava(true){}
};
int n;

int main()
{
    while(~scanf("%d", &n))
    {
        products pro[N];
        int res = 0;
        priority_queue<products> que;
        int ed = -1;

        for (int i = 0; i < n; i ++)
        {
            scanf("%d%d", &pro[i].val, &pro[i].ddl);
            ed = max(ed, pro[i].ddl);
            pro[i].id = i;
        }

        for (int i = ed; i >= 1; i --)
        {
            for (int j = 0; j < n; j ++)
            {
                if(pro[j].ddl >= i && pro[j].ava)
                {
                    que.push(pro[j]);
                }
            }

            if(que.size())
            {
                products t = que.top();
                que.pop();
                res += t.val;
                pro[t.id].ava = false;
            }
        }
        printf("%d\n", res);
    }
    return 0;
}

2. 贪心 + 并查集优化(47ms)

  1. 先把商品按照价值由大到小排列一遍
  2.  开一个数组 f [ N ],f [ i ] 表示第 i 天的祖先(就是并查集中常开的那个记录父亲的数组),首先将每一天的父亲都指向自己
  3. 从头开始遍历每一个物品你,每次遍历到第 i 个物品,查找该物品的截止日期的根节点 fd,如果大于0,就说明这个物品可以出售,并且就在 fd 这一天出售,否则说明这件物品不能出售。因为商品是按价值优先排序的,价值大的商品应该选择在截止日期 ddl 出售,然后 ddl 就被占用了,就将 ddl 的根节点指向 ddl - 1,所有之后遍历的商品如果满足截止日期是 ddl,就应该在 ddl - 1 (现在的根节点)这一天及之前出售。(看一下图吧,在代码下面🙃)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define N 10001
using namespace std;

struct products{
    int val, ddl;
    bool operator < (const products& a) const
    {
        return val > a.val;
    }
};
int n;
int f[N];

int find(int s)
{
	if(s != f[s]) f[s] = find(f[s]);
	return f[s];
}

int main()
{
    while(~scanf("%d", &n))
    {
        products pro[N];
        priority_queue<products> que;
        int ed = -1;

        for (int i = 0; i < n; i ++)
        {
            scanf("%d%d", &pro[i].val, &pro[i].ddl);
            ed = max(ed, pro[i].ddl);
        }
        sort(pro, pro + n);

        for (int i = 0; i <= ed; i ++) f[i] = i;

        int res = 0;
        for (int i = 0; i < n; i ++)
        {
        	int fd = find(pro[i].ddl);
        	if(fd > 0)
        	{
        		res += pro[i].val;
        		f[fd] = fd - 1; // 把这一天的指向前一天
        	}
        }
        printf("%d\n", res);
    }
    return 0;
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Victayria

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值