CSP-J 2023 入门级 第一轮 完善程序(1)

【题目】

CSP-J 2023 入门级 第一轮 完善程序(1)
(1)(寻找被移除的元素)问题:原有长度为n+1、公差为1的等差升序数列;将数列输入到程序的数组时移除了一个元素,导致长度为n的升序数组可能不再连续,除非被移除的是第一个或最后一个元素。需要在数组不连续时,找出被移除的元素。
试补全程序。

#include 
#include 
using namespace std;
int find_missing(vector<int>& nums) {
    int left = 0, right = nums.size() - 1;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == mid +) {} else {}
    }
    return}

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for(int i = 0; i < n; i++)cin >> nums[i];
    int missing_number = find_missing(nums);
    if (missing_number ==) {
        cout << "Sequence is consecutive" << endl;
    } else {
        cout << "Missing number is " << missing_number << endl;
    }
    return 0;
}
  1. ①处应该填( )
    A. 1 B. nums[0] C. right D. left
  2. ②处应该填( )
    A. left = mid + 1 B. right = mid – 1
    C. right = mid D. left = mid
  3. ③处应该填( )
    A. left = mid + 1 B. right = mid – 1
    C. right = mid D. left = mid
  4. ④处应该填( )
    A. left + nums[0] B. right + nums[0]
    C. mid + nums[0] D. right + 1
  5. ⑤处应该填( )
    A. nums[0]+n B. nums[0]+n-1 C. nums[0]+n+1 D. nums[n-1]

【题目考点】

1. 二分查找

【解题思路】

先看主函数

int main() {
    int n;
    cin >> n;
    vector<int> nums(n);
    for(int i = 0; i < n; i++)cin >> nums[i];
    int missing_number = find_missing(nums);
    if (missing_number ==) {
        cout << "Sequence is consecutive" << endl;
    } else {
        cout << "Missing number is " << missing_number << endl;
    }
    return 0;
}

输入n,输入n个元素保存在顺序表nums中,下标0~n-1。根据题目,等差数列公差为1,一共n+1个数字,如果被移除的不是nums[0],那么这n+1个数字最小值为nums[0],最大值应该为nums[0]+n,在这n+1个数中移除一个数字。
调用find_missing函数,查找丢失的数字,赋值给missing_number。
如果丢失的数字是(5),那么输出“序列是连续的”,否则输出丢失的数字。
也就是说如果丢失的数字是n+1个数字中的第1个或最后一个,find_missing函数会返回(5)处应该填的值,此时整个序列就是连续的。

int find_missing(vector<int>& nums) {
    int left = 0, right = nums.size() - 1;
    while (left < right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] == mid +) {} else {}
     }
     return}

而后看find_missing函数,传入是的nums的引用。看这一代码结构,有left, right, mid,自然是使用了二分查找算法。
设为nums的元素下标从0~nums.size()-1,先将left, right指向nums保存的序列的两端。
只要left < right,每次循环取中点位置mid为mid = left+(right-left)/2,该写法在数学意义上等价于mid = (left+right)/2

之所以写成mid = left+(right-left)/2,是因为这个写法可以保证即使left+right的值超过int可以表示的数值范围,仍能正确求出(left+right)/2的值

在整数域二分查找算法中,如果取中点的写法为mid = (left+right)/2,那么就是在求满足某一条件的最小值的最小下标。(反之,如果取中点的写法为mid = (left+right+1)/2,那么就是在求满足某一条件的最大值的最大下标。)
该题要找的是缺失的数字,那么二分查找的条件就可以是“大于缺失数字”,二分查找后找到的是“大于缺失数字的最小值的最小下标”。
二分查找的过程中,要始终保持要寻找的值的下标在left~right范围之内。

  • 如果mid满足条件“大于缺失数字”,则看看有没有满足条件的更小的数字,取左半边,mid满足条件应该保留在left~right范围之内,所以应该让right = mid
  • 如果mid不满足条件“大于缺失数字”,则应该找更大的数字让其满足条件,取右半边,mid不满足条件应该在left~right范围之外,所以应该让left = mid+1

设数字序列起始值为s,去掉的数字为s+m。

0 1 2 m-1 m m+1 n-1 n
原数字序列 s s+1 s+2 s+m-1 s+m s+m+1 s+n-1 s+n
去掉m后的序列:nums s s+1 s+2 s+m-1 s+m+1 s+m+2 s+n

可以看到,在下标小于m的范围内,nums[i]的值应该等于s+i,下标大于等于m时,nums[i]的值应该等于s+i-1
假设去掉的数字不是第一个数字s,起始数字s就是nums[0]nums[mid]应该与mid+nums[0]比较,(1)处应该填nums[0],选B。

  • 如果nums[mid]的值是mid+nums[0],那么mid一定小于缺失的数字,不满足条件“大于缺失数字”,应该让left = mid+1,(2)处选A。
  • 如果nums[mid]的值不是mid+nums[0],那么mid一定大于缺失的数字,满足条件“大于缺失数字”,应该让right = mid,(3)处选C。

while循环跳出后,left或right的值就是找到的是"大于缺失数字"的最小值的最小下标,该下标就是上表中的m,缺失的值应该是s+m,也就是代码中的nums[0]+leftnums[0]+right,(4)处选A或B。

如果去掉的数字就是第一个数字s,或最后一个数字,整个序列是连续的序列,无论mid为何值,都满足nums[mid]==mid+nums[0],按照上述写法运行程序,会不断的改变left,而right不变,直到while循环跳出,此时left与right的值都为right的初始值nums.size()-1,返回值应该为nums[0]+nums.size()-1
在主函数中,nums的长度nums.size()就是n,因此当整个序列是连续序列时,find_missing函数的返回值missing_number就是nums[0]+n-1,由于序列连续公差为1,nums[0]+n-1就是nums[n-1]
而当缺失的数字是大于nums[n-2],小于nums[n-1]的数字时,求大于缺失数字的最小值的最小下标,得到的结果(while循环跳出后left或right的值)也是n-1,find_missing函数返回的值也是nums[0]+n-1,此时nums[0]+n-1不为nums[n-1]
所以只有返回的nums[0]+n-1的值与nums[n-1]的值相等时,序列才是连续的。
(5)处选D。

【答案】

  1. B
  2. A
  3. C
  4. A或B
  5. D

你可能感兴趣的:(初赛题解,CSP初赛)