【题目】
CSP-S 2024 提高级 第一轮(初赛) 阅读程序(1)
1 #include <iostream> 
2 using namespace std; 
3 
4 const int N = 1000; 
5 int c[N]; 
6 
7 int logic(int x, int y) { 
8     return (x & y) ^ ((x ^ y) | (~x & y)); 
9 } 
10 void generate(int a, int b, int *c) { 
11     for (int i = 0; i < b; i++) { 
12         c[i] = logic(a, i) % (b + 1); 
13     } 
14 } 
15 void recursion(int depth, int *arr, int size) { 
16     if (depth <= 0 || size <= 1) return; 
17     int pivot = arr[0]; 
18     int i = 0, j = size - 1; 
19     while (i <= j) { 
20         while (arr[i] < pivot) i++; 
21         while (arr[j] > pivot) j--; 
22         if (i <= j) { 
23             int temp = arr[i]; 
24             arr[i] = arr[j]; 
25             arr[j] = temp; 
26             i++;j--; 
27         } 
28     } 
29     recursion(depth - 1, arr, j + 1); 
30     recursion(depth - 1, arr + i, size - i); 
31 } 
32 
33 int main() { 
34     int a, b, d; 
35     cin >> a >> b >> d; 
36     generate(a, b, c); 
37     recursion(d, c, b); 
38     for (int i = 0; i < b; i++) cout << c[i] << " "; 
39     cout<<endl; 
40 }
 
判断题:
- 当 1000>=d>=b 时,输出的序列是有序的( )
 - 当输入“5 5 1”时,输出为“1 1 5 5 5”( )
 - 假设数组 c 长度无限制,该程序所实现的算法的时间复杂度是 O(b)的( )
单选题: - 函数 int logic(int x,int y)的功能是( )
A. 按位与
B. 按位或
C. 按位异或
D. 以上都不是 - 当输入为“10 100 100”时,输出的第 100 个数是( )
A.91
B.94
C.95
D.98 
【题目考点】
1. 状态压缩
一个二进制数表示一个集合,一个x位二进制数从低位到高位分别为:第0位,第1位,…,第x-1位。
 第i位为0表示集合中不存在第i元素。
 第i位为1表示集合中存在第i元素。
 集合A、B分别由二进制数a、b表示,那么集合运算可以通过二进制数位运算来完成
 集合运算包括:
- A与B的交集 A ∩ B A\cap B A∩B:在A中且在B中的元素构成的集合
 - A与B的并集 A ∪ B A\cup B A∪B:在A中或在B中的元素构成的集合
 - A与B的差集 A − B A-B A−B:在A中但不在B中的元素构成的集合
 - B关于全集A的补集 A \ B A\backslash B A\B:B是A的子集,在A中但不在B中元素构成的集合
 
| 集合运算 | 集合运算公式 | 位运算 | 
|---|---|---|
| 取交集 | A ∩ B A\cap B A∩B | a & b | 
| 取并集 | A ∪ B A\cup B A∪B | a | b | 
| 取差集 | A − B A-B A−B | a & ~b | 
| 取B关于A的补集 | A \ B A\backslash B A\B | a ^ b | 
当B不是A的子集时,a ^ b表示的是在A中但不在B中的元素,以及在B中但不在A中元素构成的集合,即 
     
      
       
       
         ( 
        
       
         A 
        
       
         − 
        
       
         B 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         B 
        
       
         − 
        
       
         A 
        
       
         ) 
        
       
      
        (A-B)\cup (B-A) 
       
      
    (A−B)∪(B−A)或 
     
      
       
       
         ( 
        
       
         A 
        
       
         ∪ 
        
       
         B 
        
       
         ) 
        
       
         − 
        
       
         ( 
        
       
         A 
        
       
         ∩ 
        
       
         B 
        
       
         ) 
        
       
      
        (A\cup B) - (A\cap B) 
       
      
    (A∪B)−(A∩B)
 当A和B没有交集时,a ^ b表示的集合为 
     
      
       
       
         A 
        
       
         ∪ 
        
       
         B 
        
       
      
        A\cup B 
       
      
    A∪B
 
【解题思路】
7 int logic(int x, int y) { 
8     return (x & y) ^ ((x ^ y) | (~x & y)); 
9 } 
 
先看logic函数,如果仅仅从位运算角度来看,会感觉该运算十分复杂。但如果学过状态压缩,把x、y两个二进制数字当做集合X、Y的集合状态,那么~x & y就是Y与X的差集 
     
      
       
       
         Y 
        
       
         − 
        
       
         X 
        
       
      
        Y-X 
       
      
    Y−X,也就是在集合Y中但不在集合X中的元素构成的集合。
 x ^ y表示在X中但不在Y中的元素,以及在Y中但不在X中元素构成的集合,也就是 
     
      
       
       
         ( 
        
       
         X 
        
       
         − 
        
       
         Y 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         Y 
        
       
         − 
        
       
         X 
        
       
         ) 
        
       
      
        (X-Y)\cup (Y-X) 
       
      
    (X−Y)∪(Y−X)。
 (x ^ y) | (~x & y)就是求二者的并集 
     
      
       
       
         ( 
        
       
         X 
        
       
         − 
        
       
         Y 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         Y 
        
       
         − 
        
       
         X 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         Y 
        
       
         − 
        
       
         X 
        
       
         ) 
        
       
         = 
        
       
         ( 
        
       
         X 
        
       
         − 
        
       
         Y 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         Y 
        
       
         − 
        
       
         X 
        
       
         ) 
        
       
      
        (X-Y)\cup (Y-X) \cup (Y-X)=(X-Y)\cup (Y-X) 
       
      
    (X−Y)∪(Y−X)∪(Y−X)=(X−Y)∪(Y−X),而该集合也可以表示为 
     
      
       
       
         ( 
        
       
         X 
        
       
         ∪ 
        
       
         Y 
        
       
         ) 
        
       
         − 
        
       
         ( 
        
       
         X 
        
       
         ∩ 
        
       
         Y 
        
       
         ) 
        
       
      
        (X\cup Y) - (X\cap Y) 
       
      
    (X∪Y)−(X∩Y)
 x & y表示集合 
     
      
       
       
         X 
        
       
         ∩ 
        
       
         Y 
        
       
      
        X\cap Y 
       
      
    X∩Y,该集合与 
     
      
       
       
         ( 
        
       
         X 
        
       
         − 
        
       
         Y 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         Y 
        
       
         − 
        
       
         X 
        
       
         ) 
        
       
      
        (X-Y)\cup (Y-X) 
       
      
    (X−Y)∪(Y−X)没有交集,两个没有交集的集合进行按位或|,或者按位异或^运算的结果都是两集合的并集。
 因此(x & y) ^ ((x ^ y) | (~x & y))为 
     
      
       
       
         ( 
        
       
         X 
        
       
         ∩ 
        
       
         Y 
        
       
         ) 
        
       
         ∪ 
        
       
         ( 
        
       
         ( 
        
       
         X 
        
       
         ∪ 
        
       
         Y 
        
       
         ) 
        
       
         − 
        
       
         ( 
        
       
         X 
        
       
         ∩ 
        
       
         Y 
        
       
         ) 
        
       
         ) 
        
       
         = 
        
       
         X 
        
       
         ∪ 
        
       
         Y 
        
       
      
        (X\cap Y)\cup ((X\cup Y) - (X\cap Y))=X\cup Y 
       
      
    (X∩Y)∪((X∪Y)−(X∩Y))=X∪Y
 所以logic函数的返回值就是x | y。
10 void generate(int a, int b, int *c) { 
11     for (int i = 0; i < b; i++) { 
12         c[i] = logic(a, i) % (b + 1); 
13     } 
14 } 
 
generate函数就是对c数组进行赋值,使c[i]的值为(a|i)%(b+1)
15 void recursion(int depth, int *arr, int size) { 
16     if (depth <= 0 || size <= 1) return; 
17     int pivot = arr[0]; 
18     int i = 0, j = size - 1; 
19     while (i <= j) { 
20         while (arr[i] < pivot) i++; 
21         while (arr[j] > pivot) j--; 
22         if (i <= j) { 
23             int temp = arr[i]; 
24             arr[i] = arr[j]; 
25             arr[j] = temp; 
26             i++;j--; 
27         } 
28     } 
29     recursion(depth - 1, arr, j + 1); 
30     recursion(depth - 1, arr + i, size - i); 
31 } 
 
应该能看出,这是在做二路快速排序。pivot是标杆元素,选择第1个元素作为标杆元素。前面的下标i选择大于等于pivot的元素,后面的下标j选择小于等于pivot的元素,二者交换。交换后前面的元素较小,后面的元素较大,因此该函数进行的是升序排序。
 而这里增加了depth参数,当depth为0时,不再进行递归。depth是最大的递归深度,也就是说只进行最大递归深度为depth的快速排序。
33 int main() { 
34     int a, b, d; 
35     cin >> a >> b >> d; 
36     generate(a, b, c); 
37     recursion(d, c, b); 
38     for (int i = 0; i < b; i++) cout << c[i] << " "; 
39     cout<<endl; 
40 }
 
主函数中,先输入a、b、d。
 使用a、b通过generate函数生成数组c,下标从0~b-1。
 对数组c进行最大递归层数为d的快速排序,而后输出数组c。
【试题答案及解析】
判断题:
 1.当 1000>=d>=b 时,输出的序列是有序的( )
 答:T
 b是c数组元素个数,c数组长度为N是1000,因此b的值最大可以达到1000。d是快速排序的递归层数,最坏情况下,要想完成排序,递归层数会等于元素个数。当递归层数d大于等于元素个数b时,一定可以完成对数组c的排序。
2. 当输入“5 5 1”时,输出为“1 1 5 5 5”( )
 答:F
 已知a=5,b=5,d=1。
 根据 
     
      
       
       
         c 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         = 
        
       
         ( 
        
       
         a 
        
       
         ∣ 
        
       
         i 
        
       
         ) 
        
       
         % 
        
       
         ( 
        
       
         b 
        
       
         + 
        
       
         1 
        
       
         ) 
        
       
         = 
        
       
         ( 
        
       
         5 
        
       
         ∣ 
        
       
         i 
        
       
         ) 
        
       
         % 
        
       
         6 
        
       
      
        c[i] = (a|i)\%(b+1)=(5|i)\%6 
       
      
    c[i]=(a∣i)%(b+1)=(5∣i)%6,求出c数组
| i | 0 | 1 | 2 | 3 | 4 | 
|---|---|---|---|---|---|
| c[i] | 5 | 5 | 1 | 1 | 5 | 
选择c[0]为标杆元素,从前向后找第一个大于等于c[0]的元素是c[0],从后向前找第一个小于等于c[0]的元素为c[4],二者交换,由于二者的数值都为5,所以c数组从数值角度来看没有变化。
 由于递归层数d为1,因此只进行一趟选择和交换。输出应该为5 5 1 1 5。
3. 假设数组 c 长度无限制,该程序所实现的算法的时间复杂度是 O(b)的( )
 答:F
 generate函数和输出过程的复杂度都为 
     
      
       
       
         O 
        
       
         ( 
        
       
         b 
        
       
         ) 
        
       
      
        O(b) 
       
      
    O(b)
 对有b个元素的序列进行快速排序,递归层数最好情况下是 
     
      
       
       
         O 
        
       
         ( 
        
       
         l 
        
       
         o 
        
       
         g 
        
       
         b 
        
       
         ) 
        
       
      
        O(logb) 
       
      
    O(logb),最坏情况下是 
     
      
       
       
         O 
        
       
         ( 
        
       
         b 
        
       
         ) 
        
       
      
        O(b) 
       
      
    O(b),由于该问题递归层数受输入的d的限制,因此递归层数为 
     
      
       
       
         O 
        
       
         ( 
        
       
         m 
        
       
         i 
        
       
         n 
        
       
         ( 
        
       
         l 
        
       
         o 
        
       
         g 
        
       
         b 
        
       
         , 
        
       
         d 
        
       
         ) 
        
       
         ) 
        
       
         ∼ 
        
       
         O 
        
       
         ( 
        
       
         m 
        
       
         i 
        
       
         n 
        
       
         ( 
        
       
         b 
        
       
         , 
        
       
         d 
        
       
         ) 
        
       
         ) 
        
       
      
        O(min(logb, d))\sim O(min(b, d)) 
       
      
    O(min(logb,d))∼O(min(b,d))。每一层递归所遍历的元素平均有b个。因此recursion函数的时间复杂度为 
     
      
       
       
         O 
        
       
         ( 
        
       
         b 
        
       
         ∗ 
        
       
         m 
        
       
         i 
        
       
         n 
        
       
         ( 
        
       
         l 
        
       
         o 
        
       
         g 
        
       
         b 
        
       
         , 
        
       
         d 
        
       
         ) 
        
       
         ) 
        
       
         ∼ 
        
       
         O 
        
       
         ( 
        
       
         b 
        
       
         ∗ 
        
       
         m 
        
       
         i 
        
       
         n 
        
       
         ( 
        
       
         b 
        
       
         , 
        
       
         d 
        
       
         ) 
        
       
         ) 
        
       
      
        O(b*min(logb, d))\sim O(b*min(b, d)) 
       
      
    O(b∗min(logb,d))∼O(b∗min(b,d))
 该程序所实现的算法总体时间复杂度最好为 
     
      
       
       
         O 
        
       
         ( 
        
       
         b 
        
       
         log 
        
       
          
        
       
         b 
        
       
         ) 
        
       
      
        O(b\log b) 
       
      
    O(blogb),最坏为 
     
      
       
       
         O 
        
       
         ( 
        
        
        
          b 
         
        
          2 
         
        
       
         ) 
        
       
      
        O(b^2) 
       
      
    O(b2),该叙述错误。
单选题:
 4. 函数 int logic(int x,int y)的功能是( )
 A. 按位与
 B. 按位或
 C. 按位异或
 D. 以上都不是
 答:B
 根据上述分析,可知logic实现的是x | y。
 该问题也可以通过列真值表的方法得出结果
| x | y | logic(x, y) | 
|---|---|---|
| 0 | 0 | 0 | 
| 0 | 1 | 1 | 
| 1 | 0 | 1 | 
| 1 | 1 | 1 | 
| 与按位或运算的真值表相同。 | 
5. 当输入为“10 100 100”时,输出的第 100 个数是( )
 A.91
 B.94
 C.95
 D.98
 答:C
 a = 10, b = 100, d = 100
 由于 
     
      
       
       
         d 
        
       
         ≥ 
        
       
         b 
        
       
      
        d\ge b 
       
      
    d≥b,所以recursion函数对c数组进行了完整的升序快速排序,输出的第100个数也就是整个序列中的最大值。
 c数组中的元素通过公式 
     
      
       
       
         c 
        
       
         [ 
        
       
         i 
        
       
         ] 
        
       
         = 
        
       
         ( 
        
       
         a 
        
       
         ∣ 
        
       
         i 
        
       
         ) 
        
       
         % 
        
       
         ( 
        
       
         b 
        
       
         + 
        
       
         1 
        
       
         ) 
        
       
         = 
        
       
         ( 
        
       
         10 
        
       
         ∣ 
        
       
         i 
        
       
         ) 
        
       
         % 
        
       
         101 
        
       
      
        c[i] = (a|i)\%(b+1)=(10|i)\%101 
       
      
    c[i]=(a∣i)%(b+1)=(10∣i)%101生成,i是从0到99的整数。
 将10在2进制下按位权展开: 
     
      
       
       
         10 
        
       
         = 
        
       
         2 
        
       
         + 
        
        
        
          2 
         
        
          3 
         
        
       
      
        10 = 2+2^3 
       
      
    10=2+23,转为二进制数后为 
     
      
       
       
         ( 
        
       
         10 
        
        
        
          ) 
         
        
          10 
         
        
       
         = 
        
       
         ( 
        
       
         1010 
        
        
        
          ) 
         
        
          2 
         
        
       
      
        (10)_{10}=(1010)_2 
       
      
    (10)10=(1010)2
 由于c[i]是一个数模101的结果,也就是说最终结果不能超过100。
 现在需要找的是一个小于等于 
     
      
       
       
         ( 
        
       
         100 
        
        
        
          ) 
         
        
          10 
         
        
       
      
        (100)_{10} 
       
      
    (100)10的,二进制下第1位和第3位必须为1的最大的数字。
 从大到小枚举 
     
      
       
       
         ( 
        
       
         10 
        
       
         ∣ 
        
       
         i 
        
       
         ) 
        
       
      
        (10|i) 
       
      
    (10∣i)的值,看其第1位和第3位是否都为1
| 10|i十进制 | 10|i二进制 | 
|---|---|
| 100 | 1100100 | 
| 99 | 1100011 | 
| 98 | 1100010 | 
| 97 | 1100001 | 
| 96 | 1100000 | 
| 95 | 1011111 | 
因此 ( 10 ∣ i ) (10|i) (10∣i)的最大值为95,即 ( 10 ∣ i ) % 101 (10|i)\%101 (10∣i)%101,也就是c数组中的最大值为95,因此输出的第100个数为95,选C。



















