1372:小明的账单

【算法分析】

解法1:使用多重集合multiset
       多重集合multiset原理是红黑树,相同的数据在multiset中可以保存多份。数据在multiset中是有序的,默认按升序排序,multiset中第一个元素是最小值,最后一个元素是最大值。
输入n,循环n次,把每天的账单数值加入到multiset中,每天选出multiset中的最大值和最小值,输出并删掉这两个值。总添加的账单数达到10^6(每天100个,一共15000天),删除的账单数达到10^4个(每天删2个,一共15000天),红黑树中添加、删除元素的复杂度都为 O(logn),复杂度可以接受。

【参考代码】

#include<bits/stdc++.h>
using namespace std;
int main()
{
	multiset<int> ms;//multiset是C++标准模板库中的一个容器,它类似于set,但允许存储重复的元素,默认元素以升序排序。
	int n, t, m; 
	scanf("%d", &n);
	for(int i = 1; i <= n; ++i)
	{
		scanf("%d", &t);//这一行的数量 
		for(int j = 1; j <= t; ++j)
		{
			scanf("%d", &m);//输入账单钱数 
			ms.insert(m);
		}
		multiset<int>::iterator itFirst = ms.begin(), itLast = prev(ms.end());//::iterator是multiset容器的迭代器类型。itFirst是一个迭代器,它被初始化为指向ms中第一个元素的位置。itLast也是一个迭代器,它被初始化为指向ms中最后一个元素的位置。
		printf("%d %d\n", *itFirst, *itLast);
		ms.erase(itFirst); //删除itFirst
		ms.erase(itLast);  //删除itLast
	}
	return 0;
}

解法2:使用堆
       设账单类型,属性包括账单面额和账单编号。设大顶堆与小顶堆,里面保存的数据类型为账单类型。大顶堆中账单面额更大的更优先,小顶堆中账单面额更小的更优先。
       输入数据,对于输入的每个账单,生成一个账单对象,给每个账单都分配唯一的编号,并把每个账单分别加入大顶堆和小顶堆。
       设vis数组,vis[i]表示编号为i的账单是否已经被支付。(最多有15000天,每天最多100个账单,因此vis数组的长度设为1500000)
每天选择支付账单的方法如下:
       大顶堆中堆顶账单只要已经被支付,则删除堆顶,直到堆顶未被支付。那么此时堆顶账单为剩下的面额最大的账单,输出堆顶账单面额,将该账单设为已支付,删除堆顶。
       小顶堆中堆顶账单只要已经被支付,则删除堆顶,直到堆顶未被支付。那么此时堆顶账单为剩下的面额最小的账单,输出堆顶账单面额,将该账单设为已支付,删除堆顶。

【参考代码】

#include<bits/stdc++.h>
using namespace std;
#define N 1500005
struct Node
{
	int money, i;//m:账单面额 i:账单编号 
	Node(){}
	Node(int a, int b):money(a), i(b){}
};
struct SmallCmp//小顶堆比较仿函数 
{
	bool operator () (Node a, Node b)
	{
		return b.money < a.money;
	}
};
struct BigCmp//大顶堆比较仿函数 
{
	bool operator () (Node a, Node b)
	{
		return b.money > a.money;
	}
}; 
bool vis[N];//vis[i]:编号为i的账单是否已经支付 
int main()
{

	priority_queue<Node, vector<Node>, SmallCmp> small_pq;//小顶堆
	priority_queue<Node, vector<Node>, BigCmp> big_pq;//大顶堆
	int n, t, m, ind = 0;//ind:编号 
	cin >> n;
	for(int i = 1; i <= n; ++i)
	{
		cin >> t;//这一行的数量 
		for(int j = 1; j <= t; ++j)
		{
			cin >> m;//输入账单钱数 
			ind++;//编号加1 
			small_pq.push(Node(m, ind));//小顶堆添加编号为ind的面值为m的账单 
			big_pq.push(Node(m, ind));//大顶堆添加编号为ind的面值为m的账单
		}
		while(vis[small_pq.top().i])//小顶堆中不断找面值最小的账单,如果已支付则删除 
			small_pq.pop();
		cout << small_pq.top().money << ' ';
		vis[small_pq.top().i] = true;

		while(vis[big_pq.top().i])//大顶堆中不断找面值最大的账单,如果已支付则删除 
			big_pq.pop();
		cout << big_pq.top().money << '\n';
		vis[big_pq.top().i] = true;

	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值