【Nov 1P4,快排、插排还是?】黑匣子


  
  
【题目描述】
Black Box是一种原始的数据库。它可以存储一个整数数组,还有一个特别的变量i。最开始的时
候Black Box是空的,而i等于0。这个Black Box要处理一串命令。
命令只有两种:
ADD(x):把x元素放进Black Box;
GET:i加1,然后输出Black Box中第i小的数。
记住:第i小的数,就是Black Box里的数按从小到大的顺序排序后的第i个元素。
例如:
我们来演示一下一个有11个命令的命令串。(如下图所示)
【Nov 1P4,快排、插排还是?】黑匣子_第1张图片
现在要求找出对于给定的命令串的最好的处理方法。ADD和GET命令分别最多有200000个。
现在用两个整数数组来表示命令串:
1 .A( 1 ),A( 2 ),…A(M):一串将要被放进Black Box的元素。每个数都是绝对不超过2000000000
的整数,M≤ 200000 。例如上面的例子就是A = 3 , 1 , - 4 , 2 , 8 , - 1000 , 2 )。
2 .u( 1 ),u( 2 ),…u(N):表示第u(j)个元素被放进了Black Box里后就出现了一个GET命令。例如上
面的例子中的u = 1 , 2 , 2 , 6 )。输入数据不用判错。
【输入格式】
第一行,两个整数,M,N。
第二行,M个整数,表示A(
1 ) …A(M)。
第三行,N个整数,表示u(
1 )…u(N)。
【输出格式】
输出Black Box根据命令串所得出的输出串,一个数字一行。
【输入样例】
7 4
3 1 - 4 2 8 - 1000 2
1 2 6 6
【输出样例】
3
3
1
2
【数据规模】
对于30%的数据,M≤
10000
对于50%的数据,M≤
100000
对于100%的数据,M≤
200000

由于要频繁取出第X小值,很容易让人想到堆这种高效的数据结构,事实证明这种想法是正确的,G3Bensen神牛就用了一种传说中的“一个大根堆+一个小根堆”的神奇方法A掉此题。可是弱小的我在堆里徘徊了一个多小时,最后还是放弃打这条路打了快排

首先是快排的代码。只要会编程的人都会打,模拟插入的过程一个一个的插入就可以,纯粹的NlogN~但是快排中也有技巧,有的童鞋写的快排能得50分,而我的朴素快排只有30…在这里仅供参考,不再深入探讨。

 

参考代码(快排,30:

 

blackbox
   
   
1 program blackbox;
2 var
3 tot,p:longint;
4 m,n,i,j:longint;
5 a: array [ 1 .. 200000 ] of longint;
6 aa,u: array [ 1 .. 200000 ] of longint;
7 procedure qsort(l,r: longint);
8 var
9 i,j,x,y: longint;
10 begin
11 i: = l;
12 j: = r;
13 x: = a[(l + r) div 2 ];
14 repeat
15 while a[i] < x do
16 inc(i);
17 while x < a[j] do
18 dec(j);
19 if not (i > j) then
20 begin
21 y: = a[i];
22 a[i]: = a[j];
23 a[j]: = y;
24 inc(i);
25 j: = j - 1 ;
26 end ;
27 until i > j;
28 if l < j then
29 qsort(l,j);
30 if i < r then
31 qsort(i,r);
32 end ;
33 begin
34 readln(m,n);
35 for i: = 1 to m do
36 read(aa[i]);
37 for i: = 1 to n do
38 read(u[i]);
39 p: = 1 ;
40 tot: = 0 ;
41 j: = 0 ;
42 for i: = 1 to m do
43 begin
44 inc(tot);
45 a[tot]: = aa[i];
46 qsort( 1 ,tot);
47 while u[p] = i do
48 begin
49 inc(j);
50 writeln(a[j]);
51 inc(p);
52 end ;
53 if p > m then break;
54 end ;
55 end .

 

其实这个过程和插入排序很像…So我打了个插排,以为在一定情况下效率高于快排,但是还是30

 

 

参考代码(插排,30

blackbox
   
   
1 program blackbox;
2 var
3 tot,p:longint;
4 m,n,i,j:longint;
5 a: array [ 1 .. 200000 ] of longint;
6 aa,u: array [ 1 .. 200000 ] of longint;
7 procedure qsort(x:longint);
8 var
9 i,j:longint;
10 begin
11 i: = 1 ;
12 while (a[i] <= x) and (i <= tot - 1 ) do inc(i);
13 for j: = tot downto i + 1 do a[j]: = a[j - 1 ];
14 a[i]: = x;
15 end ;
16 begin
17 readln(m,n);
18 for i: = 1 to m do
19 read(aa[i]);
20 for i: = 1 to n do
21 read(u[i]);
22 p: = 1 ;
23 tot: = 0 ;
24 j: = 0 ;
25 for i: = 1 to m do
26 begin
27 inc(tot);
28 qsort(aa[i]);
29 while u[p] = i do
30 begin
31 inc(j);
32 writeln(a[j]);
33 inc(p);
34 end ;
35 if p > m then break;
36 end ;
37 end .

 

其实对付这种题还有一个利器——二叉排序树~其实建树的过程就是ADD的过程,这个谁都会,关键是找到第K小值。

我们用一个域表示当前节点的左儿子数。这样从根节点开始,如果k-1比当前节点的左儿子少,就在它的左子树中找,如果大就在右子树中找,相同就说明找到了。这样用一个递归就可以解决问题,编程复杂度很低~

 

参考代码(二叉排序树,100):

 

  
  
1 program blackbox;
2 type l = record
3 nn:longint; // nn表示这个节点的权
4 l,r,s:longint; // l是左节点,r是右节点,s是左儿子数
5 end ;
6 var
7 t,x,y,p:longint;
8 m,n,i,j:longint;
9 a: array [ 1 .. 200000 ] of l;
10 aa,u: array [ 1 .. 200000 ] of longint;
11 procedure print(x,po:longint);
12 begin
13 if a[po].s = x then
14 begin
15 writeln(a[po].nn);
16 exit;
17 end ;
18 if a[po].s > x then print(x,a[po].l); // 递归左子树
19 if a[po].s < x then print(x - a[po].s - 1 ,a[po].r); // 右子树,要把左边的节点数减掉
20 end ;
21 begin
22 readln(n,m);
23 for i: = 1 to n do read(aa[i]);
24 for i: = 1 to m do read(u[i]);
25 p: = 1 ;
26 for i: = 1 to n do
27 begin
28 a[i].nn: = aa[i];
29 t: = 1 ;
30 while i <> 1 do // 建树
31 begin
32 if a[i].nn >= a[t].nn then
33 begin
34 if a[t].r = 0 then
35 begin
36 a[t].r: = i;
37 break;
38 end
39 else t: = a[t].r;
40
41 end ;
42 if a[i].nn < a[t].nn then
43 begin
44 if a[t].l = 0 then
45 begin
46 a[t].l: = i;
47 inc(a[t].s);
48 break;
49 end
50 else begin
51 inc(a[t].s);
52 t: = a[t].l;
53 end ;
54 end ;
55 end ;
56 while u[p] = i do // 输出
57 begin
58 print(p - 1 , 1 );
59 inc(p);
60 end ;
61 end ;
62 end .

 

saltless原创,转载请注明出处)

你可能感兴趣的:(【Nov 1P4,快排、插排还是?】黑匣子)