【历题回顾】CSP-S 2020 初赛后三题详解
最后三题似乎是有点代表性的难题,就挑着讲一讲罢,毕竟我基础的选择题都全对了后面的程序前两题也是一点小失误丢分不大,,,,
第十八题
首先我们浅浅的看一看代码:
1 | // 这道题的编写者手写了几个数据结构,我们要注意一下。 |
大概阅读完就知道了这个题目的操作逻辑。
接下来看一看题目
- 判断题
输出可能为 $0$ 。( )
解析:
初始结束字符串一样就是零,一眼题,判对
若输入的两个字符串长度均为 $101$ 时,则 $m=0$ 时的输出与 $m=100$ 时的输出是一样的。( )
解析:
带入看看,m = 0 时,
[0,0]
意义不大,但是[0,100]
就意味着整体左移
当 m = 100 时,也一样的,就是[0,100]
循环右移
方向都不一样,怎么可能一样!所以判错若两个字符串的长度均为 $n$ ,则最坏情况下,此程序的时间复杂度为 $O(n!)$ 。( )
解析:
分析一波,这个广搜的状态数量,我们首先就要想想,我们如何让一个数,与另一个数交换?我们以
1234
变成1432
,其中 m = 1 为例。1
2
3
4
5
6
70: 1234
1: 2134
2: 2341
3: 2413
4: 4213
5: 4132
6: 1432事实上,通过这种以 m 点作为中心,左右循环的方式,可以使得我们把任意两个数交换。也就是说,对于由 $n$ 个不同字母构成的字符串来说,状态最多有 $n!$ 种。
然后我们在想,他那个哈希表动不动就扫描所有的,复杂度是不是也是状态数,这一下复杂度就已经 $O((n!)^2)$ 了,另外提一嘴,函数
LtoR
和RtoL
也是 $O(n)$ 的。。。总复杂度是 $O((n!)^2n)$ 。。。。判错
- 单选题
(2.5 分)若输入的第一个字符串长度由 $100$ 个不同的字符构成,第二个字符串是第一个字符串的倒序,输入的 $m$ 为 $0$,则输出为( )。
A. 49 B. 50 C. 100 D. -1
解析:
这很简单,镜像的字符串怎么可能通过整体的循环左移而对其,显而易见的无解,直接选 D
(4 分)己知当输入为
0123\n3210\n1
时输出为 $4$ ,当输入为012345\n543210\n1
时输出为 $14$ ,当输入为01234567\n76543210\n1
时输出为 $28$ ,则当输入为0123456789ab\nba9876543210\n1
输出为()。其中\n
为换行符。A. 56 B. 84 C. 102 D. 68
解析:
首先,我们要看看如何把
0123
变成3210
当 m = 1 的时候1
2
3
4
50: 0123
1: 0231
2: 2031
3: 2310
4: 3210刚好四步,来继续,我们看看当 m = 1 的时候,如何把
012345
变成543210
1
2
3
4
5
6
7
8
9
10
11
12
13
14
150: 012345
1: 023451
2: 034512
3: 304512
4: 345120
5: 351204
6: 312045
7: 132045
8: 120453
9: 104532
10: 015432
11: 045321
12: 405321
13: 450321
14: 543210总之这个很好枚举,把样例都模拟了推出答案基本上就没什么难度了,选 D
(4 分)若两个字符串的长度均为 $n$ ,且 $0<m<n−1$ ,且两个字符串的构成相同(即任何一个字符在两个字符串中出现的次数均相同),则下列说法正确的是( )。提示:考虑输入与输出有多少对字符前后顺序不一样。
A. 若 $n,m$ 均为奇数,则输出可能小于 $0$ 。
B. 若 $n,m$ 均为偶数,则输出可能小于 $0$ 。
C. 若 $n$ 为奇数、$m$ 为偶数,则输出可能小于 $0$ 。
D. 若 $n$ 为偶数、$m$ 为奇数,则输出可能小于 $0$ 。解析:
题目既然已经提示我们从逆序对分析,那么我们就看看有什么规律
考虑当 $n$ 为奇数时,假设 $st1$ 的字符严格单调递增,$st0$ 的字符严格单调递减,则 $st1$ 的逆序对为 $0$ ,$st0$ 的逆序对为 $\cfrac {n\times(n-1)}{2}$ 为奇数。由于 $m$ 为偶数,因此不论将$st0[m]$放在字符串开头或者结尾,逆序对的减少量都为偶数,最终不可能为$0$。
第十九题
(分数背包)小 S 有 $n$ 块蛋糕,编号从 $1$ 到 $n$ 。第 $i$ 块蛋糕的价值是 $w_i$ ,体积是 $v_i$ 。他有一个大小为 $B$ 的盒子来装这些蛋糕,也就是说装入盒子的蛋糕的体积总和不能超过 $B$ 。他打算选择一些蛋糕装入盒子,他希望盒子里装的蛋糕的价值之和尽量大。
为了使盒子里的蛋糕价值之和更大,他可以任意切割蛋糕。具体来说,他可以选择一个 $a(0<a<l)$ ,并将一块价值是 $w$ ,体积为 $v$ 的蛋糕切割成两块,其中一块的价值是 $a×w$ ,体积是 $a×v$ ,另一块的价值是 $(1−a)×w$ ,体积是 $(1−a)×v$ 。他可以重复无限次切割操作。
现要求编程输出最大可能的价值,以分数的形式输出。
比如 $n=3,B=8$ ,三块蛋糕的价值分别是 $4,4,2$ ,体积分别是 $5,3,2$ 。那么最优的方案就是将体积为 $5$ 的蛋糕切成两份,一份体积是 $3$ ,价值是 $2.4$ ,另一份体积是 $2$ ,价值是 $1.6$ ,然后把体积是 $3$ 的那部分和后两块蛋糕打包进盒子。最优的价值之和是 $8.4$ ,故程序输出 $\cfrac{42}{5}$ 。
输入的数据范围为:$1≤n≤1000$ ,$1≤B≤10^5$ ,$1≤w_i,v_i≤100$ 。
提示:将所有的蛋糕按照性价比 $\cfrac{w_i}{v_i}$ 可从大到小排序后进行贪心选择。
试补全程序。
1 |
|
①处应填( )
1
2
3
4A. w[j] / v[j] < w[j+1] / v[j+1]
B. w[j] / v[j] > w[j +1] / v[j+1]
C. v[j] * w[j+1] < v[j+1] * w[j]
D. w[j] * v[j+1] < w[j+1] * v[j]解析:
题目已经给出提示,这就是一个简单的冒泡排序而已,按照题目的意思不难写出:
但是说,我们毕竟不可以用
int
整形做除法,这样是算的整除,所以说,我们只能使用交叉相乘变除为乘。学过分式的人都可以写出直接选 C
②处应填( )
1
2
3
4A. w[1] <= B
B. v[1] <= B
C. w[1] >= B
D. v[1] >= B解析:
观察后面他直接 return 了。并且输出了 $\cfrac{B\times w_1}{v_1}$ 这是什么意思?显而易见就是性价比最高的物品超过了背包容量,所以我们只需要考虑这一种物品即可。那么超过就是 $v_1\gt B$ 那么上面的 if 语句就可以直接选 B 了。
③处应填( )
1
2
3
4A. print(v[1],w[1]); return 0;
B. curV = 0; curW = 0;
C. print(w[1], v[1]); return 0;
D. curV = v[1]; curW = w[1];解析:
首先,你下面的 else 都已经直接 return 了,你这个 if 再 return 是几个意思?直接排除 A,C
那么如果这个性价比最高的物品可以被背包容纳,这最好,所以说直接选了,也就是 D
④处应填( )
1
2
3
4A. curW * v[i] + curV * w[i], v[i]
B. (curW - w[i]) * v[i] + (B - curV) * w[i], v[i]
C. curW + v[i], w[i]
D. curW * v[i] + (B - curV) * w[i], v[i]解析:
看上面 if 的意思,就是能放就全放,这里放不下了,我们自然也得利益最大化,切块块放进来。
那么首先,我们以前都是整个放进来的,所以价值一定是整数,表示出来应该是一个带分数的形式,即 curW + 当前切下来的价值
当前切下来的价值,应该是首先看剩余空间能容纳多少,即 $\cfrac{B-curV}{v_i}$ 这个分数就是要留下来这个蛋糕的这么多分数,把整个的价值乘上就是
(B-curV) * w[i] / v[i]
,然后把带分数的那一部分转化过来,分子就是curW * v[i] + (B - curV) * w[i]
分母自然就是v[i]
。所以选 D
⑤处应填( )
1
2
3
4A. curW,curV
B. curW, 1
C. curV, curW
D. curV, 1解析:
乍一看好像就是这个 print 是不太可能的,但是脑子一动,猛然想到,如果这个蛋糕从来没有切过且所有蛋糕的体积加起来也可以被背包装下,是不是就不会在 for 的过程中被输出并退出。也就是说,这时候答案是个整数,那就是直接输出 curW ,当然分母是 1 选 D
第二十题
(最优子序列)取 $m=16$ ,给出长度为 $n$ 的整数序列 $a_1,a_2,…,a_n(0≤a_i<2^m)$ 。对于一个二进制数 $x$ ,定义其分值 $w(x)$ 为 $x+popcnt(x)$ ,其中 $popcnt(x)$ 表示 $x$ 二进制表示中 $1$ 的个数。对于一个子序列 $b_1,b_2,…,b_k$ ,定义其子序列分值 $S$ 为 $w(b_1⊕b_2)+w(b_2⊕b_3)+w(b_3⊕b_4)+⋯+w(b_{k−1}⊕b_k)$ 。其中 $⊕$ 表示按位异或。对于空子序列,规定其子序列分值为 $0$ 求一个子序列使得其子序列分值最大,输出这个最大值。
输入第一行包含一个整数 $n(1≤n≤40000)$ 接下来一行包含 $n$ 个整数 $a_1,a_2,⋯,a_n$ 。
提示:考虑优化朴素的动态规划算法,将前 $\cfrac m 2$ 位和后 $\cfrac m 2$ 位分开计算。
Max[x][y]
表示当前的子序列下一个位置的高 $8$ 位是 $x$ 、最后一个位置的低 $8$ 位是 $y$ 时的最大价值。
试补全程序。
1 |
|
①处应填( )
1
2
3
4A. x >>= 1
B. x ^= x &(x ^ (x + 1))
C. x -= x | -x
D. x ^= x &(x ^ (x - 1))解析:
这道题就是典型的 lowbit 与常见的不同,我们常见的应该是
x -= x & -x
或者x -= x & (x - 1)
,自己带入也可以得出不错的结果,总之很简单的就选了 D 因为这个多了一个 x ^ (x-1) 其实对于 & 运算没有影响,如果这里的异或变出来了一个 1 也会被 & 搞掉,如果是把 1 变成了 0 (似乎不太可能的样子②处应填( )
1
2
3
4A. (a & MS) << B
B. a >> B
C. a & (1 << B)
D. a & (MS << B)解析:
这道题太弱智,题目已经把第八位和高八位的定义告诉了你,你一看这个
y = a & MS
就是第八位,那 x 就只能是高八位了。也就是 选 B 。③处应填( )
1
2
3
4A. -INF
B. Max[y][x]
C. 0
D. Max[x][y]解析:
首先这时候 Max 数组都还没有赋值,所以说,BD 都可以直接排除,那么剩下来的 AC 之中就选 C 了,负数还真没必要
④处应填( )
1
2
3
4A. Max[x][z] + w(y ^ z)
B. Max[x][z] + w(a ^ z)
C. Max[x][z] + w(x ^ (z << B))
D. Max[x][z] + w(x ^ z)解析:
我们看这里的形式,显而易见就是在更新答案。我们就考虑这里的更新怎么统计。那么我们已经知道题目给出的就是高 $x$ 低 $y$ 的最大价值,我们要给他换了,就是说把低八位换了。那么造成的新的价值显而易见是
y ^ z
。⑤处应填( )
1
2
3
4A. to_max(Max[y][z], v + w(a ^ (z << B)))
B. to_max(Max[z][y], v + w((x ^ z) << B))
C. to_max(Max[z][y], v + w(a ^ (z << B)))
D. to_max(Max[x][z], v + w(y ^ z))解析:
那么上一题统计了答案,把低八位换了,这里自然是要换掉高八位。那么就首先可以排除掉 AD 他们是对于低八位的更新没有用。然后就是看 BC 选什么,显而易见的,z 作为高八位贡献也要左移。也就是说,选 B ,B选项对于 z 的贡献单独的左移了 8 位满足条件。
好了以上就是这次 2020 提高组初赛的全部内容了~