| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 数据结构与算法 -> 2022年华东师范大学计科考研复试机试题-详细题解 -> 正文阅读 |
|
[数据结构与算法]2022年华东师范大学计科考研复试机试题-详细题解 |
前言比赛两个小时,为OI赛制(可以暴力拿部分分的)。一共六道题目 A.整数排序单点时限:2.0 sec 内存限制:512MB 题面输入若干个int类型整数,将整数按照位数由大到小排序,如果位数相同,则按照整数本身从小到大排序。例如, 输入:10 -3 1 23 89 100 9 -123 输出:-123 100 10 23 89 -3 1 9 输入的整数个数最多不超过 1 0 6 10^6 106个。 输入格式在一行中输入若干个整数,整数之间用一个空格分隔。 输出格式在一行中输出排序好的整数,整数之间用一个空格分隔。 样例input
ouput
input
ouput
思路? 1.这是一类经典的条件排序问题,它就是排序问题的小变形。由于题目说明了数组长度 n = 1 0 6 n = 10^6 n=106。所以就不要自己写冒泡排序等比较慢的排序了。在C++可直接使用STL中的排序函数函数:sort()。 ? 2.就算是你会手写快排,也不建议这么做。因为一方面是比赛的时候浪费时间,另一方面是STL中的sort加了很多优化,它比你手写的快排还会更快。 使用方法
回到这道题,它的关键其实就是构造自定义排序函数。我们来展开讲一讲这一点。
所以这题的关键就是构造自定义排序函数cmp。代码如下:
总结评价这是机试第一题,难度比较小。如果平时有在刷题应该是能秒掉的。 关键条件排序 B.位运算单点时限:2.0 sec 内存限制:512MB 题面给定一个int型整数 x x x,将 x x x的二进制表示中第i位和第j位的值互换。 0 ≤ i , j ≤ 31 0 \leq i,j \leq 31 0≤i,j≤31 注意: x x x的二进制表示的最右边为第0位。 输入格式在一行中输入三个整数, x , i , j x,i,j x,i,j, 整数之间用一个空格分隔。 输出格式在一行中输出互换后的结果 样例input
ouput
input
ouput
思路思路很自然:获取 x x x二进制表示中的第 i i i位与第 j j j位。之后将他们互换即可。 问题在于:实现方法很多 1.将其转化成二进制数组a,然后模拟swap(a[i] , a[j])后再转换回十进制数 2.利用位运算的技巧去做 第一步:获得二进制第 i , j i,j i,j位置上的值。 具体的:将数 x x x右移 i / j i/j i/j位然后将其与上1。 a = ( x > > i ) & 1 a=(x >> i)\&1 a=(x>>i)&1 b = ( x > > j ) & 1 b=(x >> j)\&1 b=(x>>j)&1 第二步:互换,具体的, x i = b x_i=b xi?=b, x j = a x_j=a xj?=a 只有当 a ≠ b a \neq b a?=b 时才需要交换。而交换的结果就是取反即可。而取反我们可以利用<异或1>来完成。
实现
总结??评价:位运算的简单考察,应该要拿满分。 C.差分计数单点时限:0.5 sec 内存限制:512MB 题面给定 n n n个整数 a 1 , . . . , a n a_1,...,a_n a1?,...,an?和一个整数 x x x。求有多少有序对 ( i , j (i,j (i,j)满足 a i ? a j = x a_i-a_j= x ai??aj?=x。 输入格式第一行两个整数 n ( 1 ≤ n ≤ 2 × 1 0 6 ) , x ( ? 2 × 1 0 6 ≤ x ≤ 2 × 1 0 6 ) n(1 \leq n \leq 2 \times 10^6),x(-2 \times 10^6 \leq x \leq 2 \times 10^6) n(1≤n≤2×106),x(?2×106≤x≤2×106),分别代表整数的个数和题目中的 x x x。 第二行 n n n个用空格隔开的整数,第 i i i个代表 ? 2 × 1 0 6 ≤ a i ≤ 2 × 1 0 6 -2 \times 10^6 \leq a_i \leq 2 \times 10^6 ?2×106≤ai?≤2×106 输出格式一行一个整数,代表满足 a i ? a j = x a_i-a_j=x ai??aj?=x的有序对 ( i , j ) (i,j) (i,j)个数。 样例input
ouput
笔者说明 ( 1 ( 1 ) , 2 ( 5 ) ) , ( 1 ( 2 ) , 2 ( 5 ) ) , ( 5 ( 3 ) , 4 ( 4 ) ) (1(1),2(5)),(1(2),2(5)),(5(3),4(4)) (1(1),2(5)),(1(2),2(5)),(5(3),4(4)) 这三个有序对。 思路1.自然暴力解法??双重循环枚举 i , j i,j i,j 来计数即可,复杂度是 O ( n 2 ) O(n^2) O(n2)。但是无法拿到满分。 服务器一般一秒跑 1 e 8 1e8 1e8 次。把 n n n 带进去看看 ( 2 ? 1 0 6 ) 2 = 4 ? 1 0 12 远 大 于 1 e 8 (2 * 10^6)^2=4 * 10^{12} 远大于 1e8 (2?106)2=4?1012远大于1e8 2.前缀暴力法(正确算法的暴力版本)??考虑枚举每一个
a
j
a_j
aj?(有序对的后面那个数), 观察题目所给等式 ??不难发现,不管
j
j
j取什么值,这样的区域总是一个前缀,我们可以将前缀:
i
∈
[
1
,
j
?
1
]
i \in [1,j-1]
i∈[1,j?1]的所有
a
i
a_i
ai? 放入到一个哈希表
H
H
H 当中,然后直接查询
H
[
x
+
a
j
]
H[x+a_j]
H[x+aj?] 即可。整个过程如下所示,还是以题目样例为例子分析:
3.维护前缀法??其实咱们将表格画出来后就能够马上看到规律,意识到一个问题:前缀的变化是连续的,也就是说:第 k k k个前缀与第 k ? 1 k-1 k?1个前缀相比只是多了一个 a k a_k ak?.所以咱们完全就没必要每次都把哈希表清空然后重新构造前缀的哈希表,而只是需要每次往哈希表里插入新的值即可。那么做法其实就很显然了~ ??显然这个问题的复杂度就是 O ( n ) O(n) O(n)的 实现? 注意,在实现的过程中还是需要注意很多细节问题。
总结评价??本题正式涉及到算法思想,鉴定为竞赛入门难度。 关键??这道题的优化关键在于前缀的连续变化性。可以复用哈希表 拓展??1.将题目中的 a i ? a j = x a_i-a_j= x ai??aj?=x 改成 a i ? a j = i ? j a_i-a_j= i-j ai??aj?=i?j ??2.序列中有多少个子段的和恰好为
x
x
x,即求有多少有序对(
i
,
j
i,j
i,j)满足 D.罗马数字单点时限:2 sec 内存限制:512MB 题面罗马数字是古罗马使用的记数系统,现今仍很常见。 罗马数字有七个基本符号: I,V,X,L,C,D,M。
需要注意的是罗马数字与十进位数字的意义不同,它没有表示零的数字,与进位制无关。按照下述的规则可以表示任意正整 “标准”形式 重复次数:一个罗马数字重复几次,就表示这个数的几倍。 左加右减:
现在给出一个阿拉伯数字表示的十进制正整数,输出其对应的罗马数字。 输入格式一行十进制整数 输出格式一行字符串,表示对应的罗马数字 样例input
output
思路??这题最主要的难点就是规则繁杂。最快的理解规则方式是,先把 1 ? 10 1-10 1?10 都表示出来!
根据条件7, 1 , 2 , 3 1,2,3 1,2,3 一定是 I,II,III 对于4,根据条件7,不能是IIII,所以根据条件2得到:IV. 对于9是同理的 ??我们发现这样的规律很容易推广到10,20,…,100 与 100 , 200 , 300 , … , 1000
??有上面三张表,我们就可以表示出 [ 1 , 1000 ] [1,1000] [1,1000] 内的任何一个数。只需要拆分十进制位即可。 ??例如: 338 = 300 + 30 + 8 338=300+30+8 338=300+30+8。所以是 C C C X X X V I I I CCCXXXVIII CCCXXXVIII 实现1.存表 2.将读入的数十进制拆分 3.查表输出 代码:略 总结评价 ??一道简单的模拟题。 E.乘法单点时限:1.0 sec 内存限制:256MB 题面给出一个长度为 n n n的数列和一一个长度为 m m m的数列,可以构造得到一个 n × m n\times m n×m的矩阵C,其中 C i , j = A i × B j C_{i,j}= A_i \times B_j Ci,j?=Ai?×Bj?。 给出整数 K K K,你需要求出 C C C中的第 k k k大的数的值。 输入格式第一行输入三个整数 n , m , K n, m,K n,m,K( 1 ≤ n , m ≤ 1 0 5 , 1 ≤ K ≤ n × m 1 \leq n,m≤10^5,1\leq K \leq n \times m 1≤n,m≤105,1≤K≤n×m)。 第二行输入 n n n个空格隔开的整数 A 1 , A 2 , . . . , A n ( ? 1 0 6 ≤ A i ≤ 1 0 6 ) A_1, A_2,...,A_n(-10^6≤A_i≤10^6) A1?,A2?,...,An?(?106≤Ai?≤106) 第三行输入 m m m个空格隔开的整数 B 1 , B 2 , . . . , B m ( ? 1 0 6 ≤ B i ≤ 1 0 6 ) B_1, B_2,...,B_ m(-10^6≤B_i≤10^6) B1?,B2?,...,Bm?(?106≤Bi?≤106) 输出格式输出一行一 个整数,表示矩阵中的第 K K K大的数的值。 样例input
output
思路1.暴力?? 两重循环得到 C C C中的所有值,之后降序排序访问第 K K K个数即可。 O ( n 2 ) O(n^2) O(n2)但无法通过全部数据。 2.拓展法 ?? 因为要知道第 k k k大的数。所以自然是希望先将 A , B A,B A,B数组降序排序。如此我们知道 A 1 × B 1 A_1 \times B_1 A1?×B1? 一定是最大值。问题在于次大值是谁? A 1 × B 2 ? 与 ? A 2 × B 1 A_1 \times B_2\ 与 \ A_2 \times B_1 A1?×B2??与?A2?×B1? 都有可能,如下 A = [ 6 ? 5 ] A = [6\ 5] A=[6?5] B = [ 6 ? 1 ] B = [6\ 1] B=[6?1] 或者 A = [ 6 ? 1 ] A = [6\ 1] A=[6?1] B = [ 6 ? 5 ] B = [6\ 5] B=[6?5] ??而且我们容易知道除了这两种情况以外没有其他可能的位置能形成次大值了。所以我们把上面这个考虑两种情况的操作称之为拓展
??我相信刷过这类题的人知道过程是用大根堆维护,每次删最大数,然后拿他拓展再塞回大根堆。这样进行 k ? 1 k-1 k?1轮,堆顶就是第 k k k大的数。(力扣上的丑数II那道题也是这样做的) ??这样的算法容易感觉出来是对的,但笔者尝试说明一下算法的正确性 "大白话"证明??不妨看一个更显然的结论:在已知前 k ? 1 k-1 k?1大的乘积并且他们的方案的情况下(即知道 k ? 1 k-1 k?1个三元组 < A i × B j , i , j > <A_i \times B_j , i,j> <Ai?×Bj?,i,j>)。那么第 k k k大的乘积一定出自这 k ? 1 k-1 k?1个方案中某一个的拓展的结果。 ??这个结论很显然,因为假设第 k k k大的数为 A i × B j A_i \times B_j Ai?×Bj? ,那么它可以看作是由$A_{i-1} \times B_j $ 拓展而来。而显然 A i ? 1 × B j > A i × B j A_{i-1} \times B_j > A_i \times B_j Ai?1?×Bj?>Ai?×Bj?,所以 A i ? 1 × B j A_{i-1} \times B_j Ai?1?×Bj? 一定是前 k ? 1 k-1 k?1里的某一个。
??所以根据这个结论我们容易设计一个暴力的算法 ??用一个集合
S
S
S维护前
k
?
1
k-1
k?1大的方案。然后通过拓展操作得到
2
×
(
k
?
1
)
2 \times (k-1)
2×(k?1) 个可能的结果,从中选一个集合中没出现过的最大值,插入到集合
S
S
S中。如此往复该过程即可,如下图所示 ??但是这样的复杂度是 O ( k 2 ) O(k^2) O(k2) 的。不过由于集合 S S S中的每个元素只有可能被拓展成两个元素,它和k是多大无关。 ??比如:第1大的数 < A 1 × B 1 , 1 , 1 > <A_1 \times B_1,1,1> <A1?×B1?,1,1> 它总是会被拓展成 < A 1 × B 2 , 1 , 2 > 与 < A 2 × B 1 , 2 , 1 > <A_1 \times B_2,1,2>与<A_2 \times B_1,2,1> <A1?×B2?,1,2>与<A2?×B1?,2,1>,不管是在第几轮 ??所以将集合
S
S
S中的
<
A
1
×
B
1
,
1
,
1
>
<A_1 \times B_1,1,1>
<A1?×B1?,1,1> 替换成
<
A
1
×
B
2
,
1
,
2
>
与
<
A
2
×
B
1
,
2
,
1
>
<A_1 \times B_2,1,2>与<A_2 \times B_1,2,1>
<A1?×B2?,1,2>与<A2?×B1?,2,1> 是一步等价操作,不改变算法正确性。而且对于第
i
,
i
∈
[
1
,
k
]
i,i \in [1,k]
i,i∈[1,k]大的元素这个性质都成立。所以我们可以给所有元素进行拓展替换。所以不难发现,我们完全可以不去维护集合
S
S
S,而是直接维护待拓展集合
T
T
T,如下图所示 ??这恰恰对应着最初的算法。我们大概可以信任这个算法是对的了。因为我们上述优化的每一步都是等价操作。所以只要暴力算法是对的,最终的优化算法就是对的。
实现略 总结F.Minimum_Sum单点时限:2.0 sec 内存限制:256MB 题面你有一个序列
a
1
,
a
2
,
.
.
.
,
a
n
a_1,a_2,...,a_n
a1?,a2?,...,an?,然后给你一些区间
[
l
,
r
]
[l,r]
[l,r].对于每一个区间,你需要找到下式的最小值,对于所有可能的
x
x
x 输入格式第一行一个整数 N N N 代表序列长度。 接下来一行有 N N N个正整数 a i ( 1 ≤ a i ≤ 1 0 9 ) a_i(1 \leq a_i \leq 10^9) ai?(1≤ai?≤109),用空格隔开。 接下来一行一个整数 Q ( 1 ≤ Q ≤ 1 0 5 ) Q(1 \leq Q \leq 10^5) Q(1≤Q≤105),代表询问的区间次数。 接下来 Q Q Q行,每行一个区间 l , r ( 1 ≤ l ≤ r ≤ N ) l,r(1 \leq l \leq r \leq N) l,r(1≤l≤r≤N) 输出格式输出 Q Q Q行。每行代表对应的区间的结果。 思路最小化曼哈顿距离??1.对于给定的区间
[
l
,
r
]
[l,r]
[l,r]。我们如何确定这个
x
x
x呢?即 它的结论是: 对序列 a a a排序后 1.当 n n n是奇数时, x ? = a n / 2 + 1 x^*=a_{n / 2 +1} x?=an/2+1? , 也就是序列的中位数。 2.当 n n n是偶数的时候 x ? ∈ [ a n / 2 , a n / 2 + 1 ] x^* \in[a_{n / 2},a_{n / 2 + 1}] x?∈[an/2?,an/2+1?] ??直观的理解:当
x
x
x 在中位数的位置时,我们无论是往左移动还是往右移动,都会有超过
n
/
2
n / 2
n/2 个点在远离他。那么结果一定会变大。如下图所示 当然严格的数学证明在此:点我,这里笔者不展开了 暴力解法??所以我们有了一个暴力的做法:对于询问的区间
[
l
,
r
]
[l,r]
[l,r],拷贝一份出来对其排序,然后根据结论得到中位数。再计算值后输出。但是这个做法无法拿满分。这题本质变为1.求解区间中位数 2.求解区间关于某一值域的和即 高级数据结构优化??至此,本题变成一道可持久化值域线段树(主席树)的模板题。在结点上维护叶节点个数以及叶节点的权值和即可。
总评??总体难度不大,都是非常常见的机试题。当时考场上分数集中在 [ 200 , 300 ] [200,300] [200,300]之间。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 1:26:20- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |