【zjnu1248】小明的账单(bill) 堆的应用

小明的账单——高级

Time Limit: 3000MS

 

Memory Limit: 3000K

Total Submissions: 57

 

Accepted: 21

Description

小明在一次聚会中,不慎遗失了自己的钱包,在接下来的日子,面对小明的将是一系列的补卡手续和堆积的账单。。。
在小明的百般恳求下,老板最终同意延缓账单的支付时间。可老板又提出,必须从目前还没有支付的所有账单中选出面额最大和最小的两张,并把他们付清。还没有支付的账单会被保留到下一天。
请你帮他计算出支付的顺序。

Input

第1 行:一个正整数N(N≤15,000),表示小明补办银联卡总共的天数。
第2 行到第N+1 行:每一行描述一天中收到的帐单。先是一个非负整数M<=100,表示当天
收到的账单数,后跟M个正整数(都小于1,000,000,000),表示每张帐单的面额。
输入数据保证每天都可以支付两张帐单。

Output

输出共N行,每行两个用空格分隔的整数,分别表示当天支付的面额最小和最大的支票的面额。

Sample Input

4

3 3 6 5

2 8 2

3 7 1 7

0

Sample Output

3 6

2 8

1 7

5 7

 

题解:

第一眼看,不就是大根小根两个堆嘛。

但是后来发现有点特殊:每次取完最大和最小的后,要删除它们。

那么明显原来的方法有不足:大根堆去掉最大的,小根堆里去不掉这个元素。

那么后来我想到:1.直接坑上厉害数据结构

2.映射。

明显第2种打起来简单多了。一开始自己完完全全想太多。

映射是什么意思?映射是很好的东西,包括dij+堆一些和堆有关的总可以用上。

举个例子:

minheap={3,6,5}

maxheap={6,5,3}

设立两个数组mintomax和maxtomin,mintomax[i]表示minheap[i]所对应的maxheap里的元素位置,

maxtomin[i]表示maxheap[i]所对应的minheap里的元素位置。

那么在上述例子中:

mintomax={3,1,2}

maxtomin={2,3,1}

这样子,如果minheap里取了3,那么maxheap里就可以确定位置来去掉3了。

可能有个疑问:怎么在堆里去掉任意一个元素?

很明显,如果堆的位置x的元素去掉,把堆看做完全二叉树,

只要将最后一个元素放到x位置,然后整理以x为根的树就好了。

那么设立一个down(y)整理以y为根的树,就可以处理了。

贴上Pascal代码。

 

var n,m,i,j,x,min,max,hlen,hlen1:longint;
    mintomax,maxtomin,mh,ah:array[0..2000001] of longint;
procedure swap(var a,b:longint);
var t:longint;
begin
  t:=a; a:=b; b:=t;
end;
procedure putmin(x:longint);
var i:longint;
begin
  mh[hlen]:=x;
  i:=hlen;
  while (i>1)and(mh[i>>1]>mh[i]) do
  begin
    swap(mh[i>>1],mh[i]);
    swap(maxtomin[mintomax[i>>1]],maxtomin[mintomax[i]]);
    swap(mintomax[i>>1],mintomax[i]);
    i:=i>>1;
  end;
end;
procedure putmax(x:longint);
var i:longint;
begin
  ah[hlen1]:=x;
  i:=hlen1;
  while (i>1)and(ah[i>>1]<ah[i]) do
  begin
    swap(ah[i>>1],ah[i]);
    swap(mintomax[maxtomin[i>>1]],mintomax[maxtomin[i]]);
    swap(maxtomin[i>>1],maxtomin[i]);
    i:=i>>1;
  end;
end;
procedure delmax(x:longint);
var i,l,r,lar:longint;
begin
  swap(ah[hlen1],ah[x]);
  swap(mintomax[maxtomin[hlen1]],mintomax[maxtomin[x]]);
  swap(maxtomin[hlen1],maxtomin[x]);
  dec(hlen1);
  i:=x;
  while (i<=hlen1) do
  begin
    l:=i<<1;
    r:=i<<1+1;
    if (l<=hlen1)and(ah[l]>ah[i]) then
      lar:=l else lar:=i;
    if (r<=hlen1)and(ah[r]>ah[lar]) then
      lar:=r;
    if i=lar then exit;
    swap(ah[i],ah[lar]);
    swap(mintomax[maxtomin[lar]],mintomax[maxtomin[i]]);
    swap(maxtomin[i],maxtomin[lar]);
    i:=lar;
  end;
end;
procedure delmin(x:longint);
var i,l,r,sma:longint;
begin
  swap(mh[hlen],mh[x]);
  swap(maxtomin[mintomax[hlen]],maxtomin[mintomax[x]]);
  swap(mintomax[hlen],mintomax[x]);
  dec(hlen);
  i:=x;
  while (i<=hlen) do
  begin
    l:=i<<1;
    r:=i<<1+1;
    if (l<=hlen)and(mh[l]<mh[i]) then
      sma:=l else sma:=i;
    if (r<=hlen)and(mh[r]<mh[sma]) then
      sma:=r;
    if i=sma then exit;
    swap(mh[i],mh[sma]);
    swap(maxtomin[mintomax[sma]],maxtomin[mintomax[i]]);
    swap(mintomax[i],mintomax[sma]);
    i:=sma;
  end;
end;
begin
  readln(n);
  hlen:=0; hlen1:=0;
  for i:=1 to n do
  begin
    read(m);
    for j:=1 to m do
    begin
      read(x);
      inc(hlen); inc(hlen1);
      mintomax[hlen]:=hlen1;
      maxtomin[hlen1]:=hlen;
      putmin(x);
      putmax(x);
    end;
    min:=mh[1];
    delmin(1);
    delmax(mintomax[1]);
    max:=ah[1];
    delmin(maxtomin[1]);
    delmax(1);
    writeln(min,' ',max);
  end;
end.


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值