NJU 2019 计算机拔尖(算法)测试 解题报告

NJU 2019 计算机拔尖(算法)测试 解题报告

题目来源:仲盛老师提供的公开资料。

要准备 2022 计拔所以也自行做了一下!

感觉如果有 OI 省一水平的话,难度并不大。

1

题目 输入两个数列 X = < x 1 , x 2 , ⋯   , x n > X= X=<x1,x2,,xn> Y = < y 1 , y 2 , ⋯   , y n > Y= Y=<y1,y2,,yn>,从所有 n 2 n^2 n2 个数 x i + y j ( 1 ≤ i , j ≤ n ) x_i+y_j(1\le i, j\le n) xi+yj(1i,jn) 中 找出最大的 m ( m ≤ n ) m(m\le \sqrt n) m(mn ) 个,并从小到大输出。要求算法最坏时间复杂度为 o ( n log ⁡ n ) o(n\log n) o(nlogn)(注意是 o o o,也就是渐近严格小于)

解答

算法流程:

(1)先用 nth_element 的方法(一种基于快速排序的方法)分别得到 X , Y X, Y X,Y m m m 大的项,再对这 m m m 个数分别进行归并排序后,得到从大到小排列的 X ′ , Y ′ X', Y' X,Y 数列(项数均为 m m m)。

(2)对于 X ′ X' X 中每个数 x i ′ x'_i xi,维护一个指针 p i p_i pi(一开始 p i = 1 p_i=1 pi=1),说明下一个可以与 x i ′ x'_i xi 匹配的是 y p i ′ y'_{p_i} ypi。然后将 x i ′ + y p i ′ , ( i = 1 , 2 ⋯ n ) x'_i+y'_{p_i},(i=1, 2\cdots n) xi+ypi,(i=1,2n) 全部放入一个大根堆。

(3)每次从堆顶取出一个元素记录,并弹出。将堆顶对应的 x i ′ x'_i xi 对应的 p i p_i pi 自增 1,再放入 x i ′ + y p i ′ x'_i+y'_{p_i} xi+ypi。如此循环往复步骤(3) m m m 次。

(4)将记录下来的 m m m 个数倒着输出。

时间复杂度分析:

第(1)步中 nth_element 最坏时间复杂度为 O ( n ) O(n) O(n),排序时间复杂度为 O ( m log ⁡ m ) O(m\log m) O(mlogm);(注意这里用归并排序是因为快速排序最坏时间复杂度是 O ( m 2 ) O(m^2) O(m2) 的,不过好像也可以过)

第(2)步中,将 m m m 个元素放入堆的时间复杂度为 O ( m log ⁡ m ) O(m\log m) O(mlogm)

第(3)步中,各 m m m 次将元素弹出堆和放入堆,时间复杂度为 O ( m log ⁡ m ) O(m\log m) O(mlogm)

第(4)步中,输出复杂度为 O ( m ) O(m) O(m)

故最坏时间复杂度为 O ( n + m log ⁡ m + m ) O(n+m\log m+m) O(n+mlogm+m),由于 m ≤ n m\le \sqrt n mn ,所以最坏时间复杂度为 O ( n + n log ⁡ n + n ) = O ( n ) = o ( n log ⁡ n ) O(n+\sqrt n\log n+\sqrt n)=O(n)=o(n\log n) O(n+n logn+n )=O(n)=o(nlogn),满足题意。

思路 注意到 m ≤ n m\le \sqrt n mn 这个限制条件,然后发现只有前 m m m 大的数值得被考虑。这样自然而然用 nth_element 找出前 m m m 大,就变成一个经典题了。

2

题目 假设 A A A 是一个 n n n 元数组,其中每个元素都是不小于 1 1 1、不大于 n n n 的整数。假设 k k k 是一个小于 n n n 但大于 n 2 \dfrac{n}{2} 2n 的整数,而 I I I 则是一个 k k k 元数组。我们用 A ( I ) A(I) A(I) 来表示一个 k k k 元数组,它的第 j 个元素是 A [ I [ j ] ] A[I[j]] A[I[j]]

回忆“递增”的定义:我们说 I I I 是递增的,如果对于任何 i , j ∈ I i,j\in I i,jI,只要 i < j ii<j,就一定有 A [ i ] < A [ j ] A[i]A[i]<A[j]。如果数组 I I I 和数组 A ( I ) A(I) A(I) 都是递增的,那么我们就说 I I I 2 2 2-递增的。如果数组 I I I、数组 A ( I ) A(I) A(I) 和数组 A ( A ( I ) ) A(A(I)) A(A(I)) 都是递增的,那么我们就说 I I I 3 3 3-递增的……以此类推,对于任意正整数 r r r,我们可以定义 r r r-递增。倘若对于所有正整数 r r r I I I 都是 r r r-递增的,那么我们就说 I I I 是无穷大-递增的。

我们定义 I I I 的递增度为使得 I I I r r r-递增的最大的整数 r r r;倘若 I I I 是无穷大-递增的,那么它的递增度就是无穷大;而一个非递增数列的递增度则为 0 0 0

请设计一个尽可能高效的算法,对于输入 A A A I I I,求出递增度。你的算法在最坏情况下的时间复杂度必须为 O ( n 4 ) O(n^4) O(n4)。请给出简要的复杂度分析。

解答1 一个比较好想的思路:

算法流程

(1)将 I I I 从小到大排序,如果 I I I 存在相等元素,则递增度为 0 0 0;否则进入步骤(2)。

(2)不妨设 I I I 的元素依次为 I 1 , I 2 , ⋯   , I k I_1, I_2, \cdots, I_k I1,I2,,Ik 且依次增大。对于所有的 ( I i , I i + 1 ) , 1 ≤ i < k (I_i, I_{i+1}), 1\le i(Ii,Ii+1),1i<k 进行(3),得到 i i i 对应的“深度” d i d_i di,再进入(4)。

(3)从(2)处得到两个数 x , y x, y x,y。一开始 x ← I i , y ← I i + 1 x\gets I_i, y\gets I_{i+1} xIi,yIi+1

i. 判断是否满足 A [ x ] < A [ y ] A[x]A[x]<A[y]

ii. 由 i. 的判断分为两种情况

如果满足,判断 ( x , y ) (x,y) (x,y) 在之前的循环过程中是否出现过:若出现过,则返回深度为 + ∞ +\infin +;否则让 x ← A [ x ] , y ← A [ y ] x\gets A[x], y\gets A[y] xA[x],yA[y],并计数器增 1,继续循环(3)i. 的判断。

如果不满足,返回计数器的值。

(4)得到 k − 1 k-1 k1 d i d_i di,取所有 d i d_i di 中的最小值,即为答案。

时间复杂度分析:

(1)排序复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)

(2)中调用了 k − 1 k-1 k1 次(3)

(3)单次复杂度至多 O ( n 2 ) O(n^2) O(n2)(因为不同的 ( x , y ) (x,y) (x,y) O ( n 2 ) O(n^2) O(n2) 种)

(4)复杂度为 O ( k ) O(k) O(k)

综上,本算法复杂度为 O ( n log ⁡ n + k ⋅ n 2 + k ) = O ( n 3 ) O(n\log n+k\cdot n^2+k)=O(n^3) O(nlogn+kn2+k)=O(n3),符合题意。

思路1 仔细读题,这种“ r r r-递增”是一种迭代的过程。但是我们注意到,没有必要对整个 l l l 进行迭代,只要对 l l l 大小相邻元素进行迭代即可,取深度的最小值。

解答2 看到 解答1 的算法,明显可以优化(但是优化也没有分多qwq)

以所有的二元对 ( x , y ) , 1 ≤ x , y ≤ n (x,y),1\le x,y\le n (x,y),1x,yn 为点建图。如果 x < y xx<y A [ x ] < A [ y ] A[x]A[x]<A[y],则 ( x , y ) (x,y) (x,y) ( A [ x ] , A [ y ] ) (A[x], A[y]) (A[x],A[y]) 连有向边。定义一个点的深度为从它可以沿图继续走多少步(如果可以在环上一直走那当然深度是 + ∞ +\infin +)。那么,我们跑个 Tarjan 求 SCC,那么所有可以到点数 ≥ 2 \ge 2 2 的 SCC 的点深度都是 + ∞ +\infin +

然后我们再向 解答1 一样 k − 1 k-1 k1 项判断即可。总时间复杂度为 O ( n 2 ) O(n^2) O(n2)。甚至可以对 I I I 多测,单次复杂度 O ( n ) O(n) O(n)

但没有分多为啥要写这种无聊的玩意啊!!!

你可能感兴趣的:(NJU,题解,算法,计拔)