【算法分析】
解法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;
}
744

被折叠的 条评论
为什么被折叠?



