【题目】
CSP-S 2022 提高级 第一轮 阅读程序(3)
1 #include <iostream>
2 #include <algorithm>
3
4 using namespace std;
5
6 const int MAXL = 1000;
7
8 int n, k, ans[MAXL];
9
10 int main(void)
11 {
12 cin >> n >> k;
13 if (!n) cout << 0 << endl;
14 else
15 {
16 int m = 0;
17 while (n)
18 {
19 ans[m++] = (n % (-k) + k) % k;
20 n = (ans[m - 1] - n) / k;
21 }
22 for (int i = m - 1; i >= 0; i--)
23 cout << char(ans[i] >= 10 ?
24 ans[i] + 'A' - 10 :
25 ans[i] + '0');
26 cout << endl;
27 }
28 return 0;
29 }
假设输入的 n 在 int 范围内,k 为不小于 2 且不大于 36 的正整数,完成下面的判断题和单选题:
判断题
1.该算法的时间复杂度为
O
(
log
k
n
)
O(\log_k n)
O(logkn)
2.删除第 23 行的强制类型转换,程序的行为不变。
3.除非输入的 n 为 0,否则程序输出的字符数为
O
(
⌊
log
k
∣
n
∣
⌋
+
1
)
O(\lfloor \log_k|n|\rfloor+1)
O(⌊logk∣n∣⌋+1)
(注:洛谷上此处的题不对,原卷纸上是
O
(
⌊
log
k
∣
n
∣
⌋
+
1
)
O(\lfloor \log_k|n|\rfloor+1)
O(⌊logk∣n∣⌋+1))
试卷截图:
单选题
4.当输入为“100 7”时,输出为( )。
A. 202
B. 1515
C. 244
D. 1754
5.当输入为“-255 8”时,输出为( )。
A. 1400
B. 1401
C. 417
D. 400
6.当输入为“1000000 19”时,输出为( )。
A. BG939
B. 87GIB
C. 1CD428
D. 7CF1B
【题目考点】
1. 数学取模
数学取模相关概念
证明:C++中取模运算为
%
\%
%,一定有
a
%
b
a\%b
a%b等于
a
%
(
−
b
)
a\%(-b)
a%(−b)
已知C++中除法运算的商为向0取整,设a除以b的商向0取整后为q。
所以有
a
=
q
∗
b
+
r
a = q*b+r
a=q∗b+r,其中r为
a
%
b
a\%b
a%b
设a除以b的实数商为c,c向零取整为q。
则a除以-b的实数商为-c,-c和c是相反数,相反数向零取整的结果也互为相反数,所以-c向零取整为-q。
根据
a
=
q
∗
b
+
r
a = q*b+r
a=q∗b+r有
a
=
(
−
q
)
∗
(
−
b
)
+
r
a = (-q)*(-b)+r
a=(−q)∗(−b)+r
其中-q是a除以-b向零取整的结果,r即为
a
%
(
−
b
)
a\%(-b)
a%(−b)
所以
a
%
b
a\%b
a%b等于
a
%
(
−
b
)
a\%(-b)
a%(−b)
【解题思路】
10 int main(void)
11 {
12 cin >> n >> k;
13 if (!n) cout << 0 << endl;
14 else
15 {
16 int m = 0;
17 while (n)
18 {
19 ans[m++] = (n % (-k) + k) % k;
20 n = (ans[m - 1] - n) / k;
21 }
22 for (int i = m - 1; i >= 0; i--)
23 cout << char(ans[i] >= 10 ?
24 ans[i] + 'A' - 10 :
25 ans[i] + '0');
26 cout << endl;
27 }
28 return 0;
29 }
输入n和k,n为0时就输出0。
只要n不为0就进行循环。
ans[m++]=(n%(-k)+k)
,结合m初值为0,可以看出这是在做数组填充,填充的数在ans数组中的下标范围是0~m-1。
首先n%(-k)
等于n%k
(见【题目考点】)。
题目给定k是正整数,而(n%k+k)%k
,就是在求数学取模
n
m
o
d
k
n\ mod\ k
n mod k的结果(见数学取模相关概念)
所以这一句的意义是:将
n
m
o
d
k
n\ mod\ k
n mod k填充到ans数组中。
下一句n=(ans[m-1]-n)/k;
,可以转为n=-(n-ans[m-1])/k;
ans[m-1]
就是上一次填充到数组中的
n
m
o
d
k
n\ mod\ k
n mod k,根据除法定理,一定有商q满足
n
=
k
∗
q
+
(
n
m
o
d
k
)
n=k*q+(n\ mod\ k)
n=k∗q+(n mod k),q是
⌊
n
k
⌋
\lfloor \frac{n}{k} \rfloor
⌊kn⌋。那么
(
n
−
(
n
m
o
d
k
)
)
/
k
(n - (n\ mod\ k))/k
(n−(n mod k))/k就是q,即
⌊
n
k
⌋
\lfloor \frac{n}{k} \rfloor
⌊kn⌋。
所以n=(ans[m-1]-n)/k;
所做的运算为把n变为
−
⌊
n
k
⌋
-\lfloor \frac{n}{k} \rfloor
−⌊kn⌋。
以上过程是将n在k进制下进行数位分离,但是每分离出一位后,该数值的符号取反。将分离出的各个数位填入ans数组中。其中较小下标保存低位数字,较大下标保存高位数字。
而后从高位到低位遍历,输出分离出的各个数字,对于大于等于10的数字,将其转为大写字母输出,其中’A’表示10,'B’表示11,……,'Z’表示35。
【试题答案及解析】
判断题
1.该算法的时间复杂度为
O
(
log
k
n
)
O(\log_k n)
O(logkn)
答:T
m是数值是n在k进制下的位数,其值
m
=
⌊
log
k
n
⌋
+
1
m=\lfloor \log_k n\rfloor+1
m=⌊logkn⌋+1。17行的while循环和22行的for循环的循环次数都是m次。总体时间复杂度为
O
(
m
)
=
O
(
log
k
n
)
O(m)=O(\log_k n)
O(m)=O(logkn)
2.删除第 23 行的强制类型转换,程序的行为不变。
答:F
由于ans数组为int类型,int类型和char类型量进行运算的表达式的类型也是int类型。因此ans[i]+'A'-10
和ans[i]+'0'
的值都是int类型。整个三目运算表达式ans[i]>=10 ? ans[i]+'A'-10 : ans[i]+'0'
的值的类型也是int类型。
cout输出int类型的值,会输出整数,不会输出字符。强转char类型后,用cout输出可以输出字符。因此去掉强转char类型后,输出会改变。
3.除非输入的 n 为 0,否则程序输出的字符数为
O
(
⌊
log
k
∣
n
∣
⌋
+
1
)
O(\lfloor \log_k|n|\rfloor+1)
O(⌊logk∣n∣⌋+1)
答:T
首先,根据大O表示法的计算规则,有:
O
(
⌊
log
k
∣
n
∣
⌋
+
1
)
=
O
(
l
o
g
∣
n
∣
)
O(\lfloor \log_k|n|\rfloor+1)=O(log|n|)
O(⌊logk∣n∣⌋+1)=O(log∣n∣)
每次循环输出一个字符,每次n的变化为
n
=
−
⌊
n
k
⌋
n=-\lfloor \frac{n}{k} \rfloor
n=−⌊kn⌋
如果
n
=
⌊
n
k
⌋
n=\lfloor \frac{n}{k} \rfloor
n=⌊kn⌋,该操作执行的次数就是n的位数,根据n在k进制下的位数公式,以及对数运算中真数必须大于0,该数字的位数,也就是循环次数应该为:
⌊
log
k
∣
n
∣
⌋
+
1
\lfloor \log_k |n|\rfloor+1
⌊logk∣n∣⌋+1
而该问题中
n
=
−
⌊
n
k
⌋
n=-\lfloor \frac{n}{k} \rfloor
n=−⌊kn⌋。考虑n的绝对值的变化,以下是定性分析:
n为正数时,
∣
n
∣
|n|
∣n∣变为
⌊
∣
n
∣
k
⌋
\lfloor \frac{|n|}{k} \rfloor
⌊k∣n∣⌋
n为负数时,
∣
n
∣
|n|
∣n∣最大可以变为
⌊
∣
n
∣
k
⌋
+
1
\lfloor \frac{|n|}{k} \rfloor+1
⌊k∣n∣⌋+1
当
∣
n
∣
≥
2
,
k
≥
2
|n|\ge 2,k\ge 2
∣n∣≥2,k≥2时,可以认为
⌊
∣
n
∣
k
⌋
+
1
≤
∣
n
∣
\lfloor \frac{|n|}{k} \rfloor+1\le |n|
⌊k∣n∣⌋+1≤∣n∣
由于n的符号每次循环都会编号,因此可以保证至少每2次循环后
∣
n
∣
|n|
∣n∣会小于等于
⌊
∣
n
∣
k
⌋
\lfloor \frac{|n|}{k} \rfloor
⌊k∣n∣⌋,当
∣
n
∣
|n|
∣n∣为0时跳出循环。该问题的循环次数不会超过
n
=
⌊
n
k
⌋
n=\lfloor \frac{n}{k} \rfloor
n=⌊kn⌋问题的循环次数的2倍,也就是
2
(
⌊
log
k
∣
n
∣
⌋
+
1
)
2(\lfloor \log_k |n|\rfloor+1)
2(⌊logk∣n∣⌋+1),
O
(
2
(
⌊
log
k
∣
n
∣
⌋
+
1
)
)
=
O
(
l
o
g
∣
n
∣
)
O(2(\lfloor \log_k |n|\rfloor+1)) = O(log|n|)
O(2(⌊logk∣n∣⌋+1))=O(log∣n∣)
单选题
4.当输入为“100 7”时,输出为( )。
A. 202
B. 1515
C. 244
D. 1754
答:A
该问题的计算方法为:每次取
n
m
o
d
k
n\ mod\ k
n mod k,而后n变为n除以k的商并将符号取反,将取出的数字从后向前排列,即为结果。
其中mod运算为数学取模,即为求n/k的商向下取整时的余数,保证余数为非负整数。
除法式 | 商 | 余数 |
---|---|---|
100/7 | 14 | 2 |
-14/7 | -2 | 0 |
2/7 | 0 | 2 |
5.当输入为“-255 8”时,输出为( )。
A. 1400
B. 1401
C. 417
D. 400
答:B
除法式 | 商 | 余数 |
---|---|---|
-255/8 | -32 | 1 |
32/8 | 4 | 0 |
-4/8 | -1 | 4 |
1/8 | 0 | 1 |
结果为1401
6.当输入为“1000000 19”时,输出为( )。
A. BG939
B. 87GIB
C. 1CD428
D. 7CF1B
答:B
除法式 | 商 | 余数 |
---|---|---|
1000000/19 | 52631 | 11(B) |
-52631/19 | 2771 | 18(I) |
算出最后两位就能确定答案是B。