超市(并查集和优先队列)

超市(并查集和优先队列)

原题链接 https://www.acwing.com/problem/content/description/147/

超市里有N件商品,每件商品都有利润 p i p_i pi和过期时间 d i d_i di 每天只能卖一件商品,过期商品不能再卖。

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

输入格式

输入包含多组测试用例。

每组测试用例,以输入整数N开始,接下来输入N对 p i p_i pi d i d_i di,分别代表第i件商品的利润和过期时间。

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

输出格式

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

每个结果占一行。

数据范围

0≤N≤100000
1≤ p i p_i pi, d i d_i di≤10000
最多有14组测试样例

输入样例:
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

一 优先队列

将n件商品按过期时间从小到大排序,然后不断的往优先队列里面插入,分两种情况插入。
优先队列里数的个数也就是表示卖出的商品数也就是天数 size

如果到了第i个数插入,

1,size< d i d_i di 直接插入,因为这时候你经过的天数并没有到你的保质期

2,size>= d i d_i di 这时候将第i个商品的利润和队列里利润最小的一个进行比较,利润大的留在队列里。

因为将过期时间按从小到大排序了,所以如果 d i d_i di <=size 的话 d i d_i di肯定等于size;

#include <bits/stdc++.h>
using namespace std;
const int N=10100;
priority_queue<int,vector<int>,greater<int> > p;//小顶堆 
pair<int,int> a[N];
int n,m,i,j;
int main()
{
    while(scanf("%d",&n)!=EOF)
    {
        if (n==0)
        {
            puts("0");
            continue;
        }
        for(int i=1; i<=n; i++)
        {
            cin>>a[i].second>>a[i].first;//pair排序是以first为第一关键字,因为我们是时间为第一关键字,所以我们读入权值是第二关键字
        }
        sort(a+1,a+1+n);//将日期从小到大排序 
        for(int i=1; i<=n; i++)
        {
            if (a[i].first>p.size())
                p.push(a[i].second);
            else if(a[i].first==p.size() && a[i].second>p.top())
            {
                p.pop();
                p.push(a[i].second);
            }
        }
        long long ans=0;
        while(!p.empty())
        {
            ans+=p.top();
            p.pop();
        }
        printf("%lld\n",ans);
    }
    return 0;
}


二、并查集

这个思路比优先队列快了三倍

首先我们将利润从大到小排序,每次扫描到一个商品;往前找空位,如果有的话就可以卖出去,否则不能

f[i]表示第i个数前面的空位,这个并查集就是将空位和商品连起来

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 233;
pair<int,int> p[maxn];
int f[maxn];//f[i]表示第i个商品前的空位 
int ff(int x)//查找祖宗; 
{
    if(f[x] == x) return x;
    return f[x] = ff(f[x]);
}
bool cmp(pair<int ,int>a,pair<int ,int>b)
{
	return a.first>=b.first;
}
//并查集是将空位和第i个数连通 
int main()
{
    int n;
    while(cin >> n)
    {
        int ans = 0;


        for(int i = 0; i < n; i++)
        {
            cin>>p[i].first>>p[i].second;
        }
        for(int i = 0; i <= maxn; i++) f[i] = i;
        sort(p,p+n,cmp);
        for(int i = 0; i <n; i++)
        {
            int v = p[i].first, e = p[i].second;
            int pos = ff(e);//查找前面的空位 
            if(pos > 0)//有空位 
            {
                ans += v;
                f[pos] = pos - 1;//空位往前移动 
            }
        }
        printf("%d\n", ans);
    }
}

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值