[算法竞赛进阶指南] 超市 (贪心+并查集/优先队列)

题目

超市里有N件商品,每个商品都有利润pi和过期时间di,每天只能卖一件商品,过期商品(即当天di<=0)不能再卖。

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

输入格式

输入包含多组测试用例。

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

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

输出格式

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

每个结果占一行。

数据范围

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

输入样例:
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
分析
  • 这个题目,我们可以运用贪心的思想来解决,对于价值高的我们能放的都放进去。这个题可以有多种解法,我这里只简要介绍两种:
  • 优先队列解法和并查集解法
  • 第一种解法:按照天数来从小到大排序,用小顶堆优先队列来维护,当优先队列的元素个数小于当前遍历到的i商品的过期天数少数,就将它放入,如果元素个数等于现在的天数时,如果现在的商品的价值大于优先队列中队顶元素时,那么我们就将队顶元素出队,将该商品的加入队列,我们知道,因为去掉队顶以后队里元素是小于过期天数的,因此这个商品可以加入,贪心方案正确。
代码:
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <limits.h> 
using namespace std;
const int N=1e4+5;

priority_queue<int,vector<int>,greater<int> > q;
int n;
pair<int,int> p[N];

int main()
{
	while(cin>>n){
		int maxd=INT_MAX;
		for(int i=0;i<n;i++){
			cin>>p[i].second>>p[i].first;
		}
		sort(p,p+n);
		for(int i=0;i<n;i++){
			if(q.size()<p[i].first) q.push(p[i].second);
			else {
				if(p[i].second>q.top()){
					q.pop();
					q.push(p[i].second);
				}
			}
		}
		int ans=0;
		while(!q.empty()){
			ans+=q.top();
			q.pop();
		}
		cout<<ans<<endl;				
	}
	return 0;
}
  • 第二种做法,将商品按价值排序,每次将价值最多的试图加入他过期前某一天中,这个某一天用并查集来维护,i点代表第i天,即用并查集来标记这个当前点或者前面哪个点是空的。如果是零,那么就是当前点前面没有为空的点,代表全部已经装满,初始化的时候将每个点指向自己,代表前面第一个为空的点是自己,因为我们是从最大价值枚举的,因此后面不能装下的一定不是最优的。
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N=1e4+5,M=10000;

int pre[N];
int find(int x){
	if(pre[x]!=x) pre[x]=find(pre[x]);
	return pre[x]; 
}

int n;
pair<int,int> p[N];

int main()
{
	ios::sync_with_stdio(false);
	while(cin>>n){
		for(int i=0;i<n;i++){
			cin>>p[i].first>>p[i].second;
		}
		sort(p,p+n);
		int ans=0; 
		for(int i=0;i<=M;i++) pre[i]=i; 
		for(int i=n-1;~i;i--){
			int v=p[i].first,w=p[i].second;
			int f=find(w);
			if(f>0){
				ans+=v;
				pre[f]=f-1;
			}
		}
		cout<<ans<<endl;
	}
	return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值