CCF GESP C++ G5编程题总结分析+备考建议+常考算法模板

CCF GESP C++ G5编程题总结分析+备考建议+常考算法模板

一、编程题核心总结与分析

(一)编程题整体特征

GESP C++五级编程题主要围绕数论、高精度运算、链表、排序、贪心、分治、递归七大核心模块展开,整体难度适中,注重基础算法的灵活运用,而非复杂的逻辑推导。题目通常分为两类典型题型,对应考试中的编程题1和编程题2,具体特征如下:

(二)编程题1(基础送分题,难度偏低)

  1. 核心考点:单一算法的直接应用,无复杂逻辑嵌套,主要考察代码模板的记忆与基本语法的规范性。
  2. 高频题型
    • 数论基础:最大公约数/最小公倍数求解、素数判断、简单质因数分解
    • 简单排序:归并排序/快速排序的直接实现、有序数组的二分查找
    • 基础递归:斐波那契数列(记忆化)、阶乘计算、字符串反转
    • 链表基础:单链表的插入/删除/遍历、循环链表的环判断
  3. 题型分析
    • 输入输出:格式简单,多为单一整数/数组输入,结果直接输出,无需复杂的输入处理逻辑
    • 代码量:通常30-50行,可直接套用核心模板,仅需少量修改适配题目要求
    • 易错点:边界条件(如n=0/1、空链表、数组越界)、数据类型溢出(如使用long long存储阶乘/斐波那契数列)

(三)编程题2(中档提升题,难度中等)

  1. 核心考点:多个算法的组合应用,需要一定的逻辑分析能力,考察对算法本质的理解而非单纯模板记忆。
  2. 高频题型
    • 高精度运算:高精度加/减/乘/除的组合使用(如大整数加法+乘法、大整数除法求余数)
    • 贪心算法:活动安排、硬币找零、分糖果、区间覆盖等经典场景的拓展
    • 分治算法:归并排序的拓展应用、最大子数组和、基于分治的数组问题求解
    • 数论综合:埃氏筛/线性筛+唯一分解定理、辗转相除法+最小公倍数的综合应用
    • 链表进阶:双链表的插入/删除、链表的反转、循环链表的应用
  3. 题型分析
    • 输入输出:格式相对复杂,多为多组数据输入、结构化数据输入(如活动的开始/结束时间),需要按指定格式输出结果
    • 代码量:通常60-100行,需要在核心模板基础上进行逻辑拓展,如添加条件判断、循环控制等
    • 易错点:算法选择错误(如非标准硬币面值误用贪心算法)、逻辑嵌套错误(如二分答案的边界收缩逻辑)、内存管理(如链表的内存释放避免内存泄漏)

(四)编程题高频考点占比

核心模块 编程题1占比 编程题2占比 总体占比
数论基础 35% 20% 27.5%
高精度运算 5% 25% 15%
链表操作 20% 15% 17.5%
排序/查找算法 20% 10% 15%
贪心/分治/递归 20% 30% 25%

二、备考建议

(一)基础阶段(优先掌握,保底编程题1得分)

  1. 熟记核心模板:将数论、排序、二分查找、基础递归、单链表的模板代码烂熟于心,能够不看参考写出完整代码,重点关注边界条件的处理。
  2. 针对性刷题:每个基础模板对应5-10道简单题,如:
    • 辗转相除法:求解不同场景下的最大公约数/最小公倍数(正数、负数、多组数)
    • 素数判断:单独素数判断、区间内素数统计
    • 二分查找:有序数组的目标查找、左/右边界查找
    • 单链表:头部/尾部插入、指定值删除、遍历打印
  3. 规范代码格式:养成良好的编码习惯,如变量命名清晰、添加注释、缩进统一,避免因格式问题导致编译错误。

(二)提升阶段(突破编程题2,拉高总分)

  1. 掌握算法组合逻辑:重点理解高频组合场景的解题思路,如:
    • 高精度运算:字符串转逆序数组→执行运算→逆序数组转字符串的固定流程
    • 贪心算法:先排序(按结束时间/面值/奖励等)→局部最优选择→验证结果的逻辑
    • 分治算法:分解子问题→求解子问题→合并子问题结果的三大步骤
    • 数论综合:先筛素数表→再进行质因数分解→最后求解约数/倍数相关问题
  2. 攻克易错点:整理自己的错题本,重点记录以下类型错误:
    • 数据类型溢出:如intlong long、高精度运算中使用1LL强制类型转换
    • 边界条件遗漏:如二分查找的left <= right vs left < right、链表的空指针判断
    • 算法选择错误:如非标准硬币面值用贪心(应改用动态规划)、无序数组用二分查找
  3. 模拟考试场景:限时完成编程题2(建议40-50分钟/题),训练快速读题、快速选择算法、快速编写代码的能力,同时练习输入输出格式的处理。

(三)冲刺阶段(查漏补缺,巩固提升)

  1. 真题演练:刷近3-5年的GESP C++五级真题,重点分析编程题的考点分布和解题思路,总结高频题型的固定解法。
  2. 模板灵活化:对核心模板进行变形训练,如:
    • 归并排序:从升序排序改为降序排序、从数组排序改为链表排序
    • 二分查找:从迭代版改为递归版、从查找目标值改为查找最值
    • 贪心算法:从活动安排的最多不重叠改为最大时间覆盖
  3. 查漏补缺:针对自己的薄弱模块(如高精度除法、双链表操作)进行专项训练,确保每个核心模块都无知识盲区。

三、常考算法模板总结(精华版)

(一)数论类(高频必考)

  1. 辗转相除法(最大公约数+最小公倍数)

    考试出现情况:2023年3月 编程题1第1题、2023年9月 编程题1第2题、2024年3月 编程题1第1题

    #include <algorithm>
    using namespace std;
    
    // 迭代版(优先使用,避免栈溢出)
    int gcd(int a, int b) {
        a = abs(a);
        b = abs(b);
        while (b != 0) {
            int temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
    
    // 最小公倍数(先除后乘,避免溢出)
    int lcm(int a, int b) {
        if (a == 0 || b == 0) return 0;
        return a / gcd(a, b) * b;
    }
    
  2. 素数判断(高效版)

    考试出现情况:2023年12月 编程题1第2题、2024年6月 编程题1第1题、2024年9月 编程题1第2题

    #include <cmath>
    using namespace std;
    
    bool is_prime(int num) {
        if (num < 2) return false;
        if (num == 2 || num == 3) return true;
        if (num % 2 == 0 || num % 3 == 0) return false;
        int end = sqrt(num) + 1;
        for (int i = 5; i <= end; i += 6) {
            if (num % i == 0 || num % (i + 2) == 0) {
                return false;
            }
        }
        return true;
    }
    
  3. 线性筛法(生成素数表)

    考试出现情况:2023年9月 编程题2第1题、2024年3月 编程题2第2题、2024年9月 编程题2第1题

    #include <vector>
    using namespace std;
    
    vector<int> sieve_linear(int n) {
        vector<bool> is_prime(n + 1, true);
        vector<int> primes;
        is_prime[0] = is_prime[1] = false;
        for (int i = 2; i <= n; ++i) {
            if (is_prime[i]) primes.push_back(i);
            for (int j = 0; j < primes.size() && 1LL * i * primes[j] <= n; ++j) {
                is_prime[i * primes[j]] = false;
                if (i % primes[j] == 0) break;
            }
        }
        return primes;
    }
    
  4. 质因数分解(唯一分解定理)

    考试出现情况:2023年6月 编程题2第2题、2024年6月 编程题2第1题、2024年12月 编程题2第2题

    #include <vector>
    #include <cmath>
    using namespace std;
    
    vector<pair<int, int>> prime_factorization(int n) {
        vector<pair<int, int>> factors;
        if (n <= 1) return factors;
        // 处理2的因子
        int cnt = 0;
        while (n % 2 == 0) { cnt++; n /= 2; }
        if (cnt > 0) factors.emplace_back(2, cnt);
        // 处理奇数因子
        for (int i = 3; i <= sqrt(n); i += 2) {
            cnt = 0;
            while (n % i == 0) { cnt++; n /= i; }
            if (cnt > 0) factors.emplace_back(i, cnt);
        }
        if (n > 2) factors.emplace_back(n, 1);
        return factors;
    }
    

(二)高精度运算类(编程题2高频)

  1. 高精度加法(核心模板)

    考试出现情况:2023年3月 编程题2第1题、2023年12月 编程题2第1题、2024年9月 编程题2第2题

    #include <vector>
    #include <algorithm>
    using namespace std;
    
    // 辅助函数:字符串转逆序数组
    vector<int> str_to_rev_arr(string s) {
        vector<int> res;
        for (int i = s.size() - 1; i >= 0; --i) res.push_back(s[i] - '0');
        return res;
    }
    
    // 辅助函数:逆序数组转字符串
    string rev_arr_to_str(vector<int> arr) {
        string res;
        while (arr.size() > 1 && arr.back() == 0) arr.pop_back();
        for (int i = arr.size() - 1; i >= 0; --i) res.push_back(arr[i] + '0');
        return res;
    }
    
    // 高精度加法(a、b为逆序数组)
    vector<int> high_precision_add(vector<int> a, vector<int> b) {
        vector<int> res;
        int carry = 0, i = 0;
        while (i < a.size() || i < b.size() || carry > 0) {
            int sum = carry;
            if (i < a.size()) sum += a[i];
            if (i < b.size()) sum += b[i];
            res.push_back(sum % 10);
            carry = sum / 10;
            i++;
        }
        return res;
    }
    
  2. 高精度减法(a >= b,逆序数组)

    考试出现情况:2023年6月 编程题2第1题、2024年3月 编程题2第1题、2024年12月 编程题2第1题

    #include <vector>
    using namespace std;
    
    vector<int> high_precision_sub(vector<int> a, vector<int> b) {
        vector<int> res;
        int borrow = 0, i = 0;
        while (i < a.size() || i < b.size()) {
            int sub = a[i] - borrow;
            if (i < b.size()) sub -= b[i];
            if (sub < 0) { sub += 10; borrow = 1; }
            else borrow = 0;
            res.push_back(sub);
            i++;
        }
        while (res.size() > 1 && res.back() == 0) res.pop_back();
        return res;
    }
    
  3. 高精度乘小整数(逆序数组)

    考试出现情况:2023年12月 编程题2第2题、2023年9月 编程题2第2题、2024年6月 编程题2第2题

    #include <vector>
    using namespace std;
    
    vector<int> high_precision_mul_small(vector<int> a, int b) {
        vector<int> res;
        int carry = 0;
        for (int i = 0; i < a.size() || carry > 0; ++i) {
            long long product = carry;
            if (i < a.size()) product += 1LL * a[i] * b;
            res.push_back(product % 10);
            carry = product / 10;
        }
        while (res.size() > 1 && res.back() == 0) res.pop_back();
        return res;
    }
    

(三)链表类(基础+进阶)

  1. 单链表基本操作(插入+删除+遍历)

    考试出现情况:2023年12月 编程题1第1题、2023年3月 编程题1第2题、2024年3月 编程题1第2题

    #include <iostream>
    using namespace std;
    
    struct ListNode {
        int val;
        ListNode* next;
        ListNode(int x) : val(x), next(nullptr) {}
    };
    
    // 头部插入
    ListNode* insert_head(ListNode* head, int val) {
        ListNode* new_node = new ListNode(val);
        new_node->next = head;
        return new_node;
    }
    
    // 尾部插入
    ListNode* insert_tail(ListNode* head, int val) {
        ListNode* new_node = new ListNode(val);
        if (!head) return new_node;
        ListNode* curr = head;
        while (curr->next) curr = curr->next;
        curr->next = new_node;
        return head;
    }
    
    // 删除指定值节点(虚拟头节点优化)
    ListNode* remove_elements(ListNode* head, int val) {
        ListNode* dummy = new ListNode(0);
        dummy->next = head;
        ListNode* curr = dummy;
        while (curr->next) {
            if (curr->next->val == val) {
                ListNode* temp = curr->next;
                curr->next = curr->next->next;
                delete temp;
            } else {
                curr = curr->next;
            }
        }
        ListNode* new_head = dummy->next;
        delete dummy;
        return new_head;
    }
    
    // 遍历打印
    void traverse_list(ListNode* head) {
        ListNode* curr = head;
        while (curr) {
            cout << curr->val << " ";
            curr = curr->next;
        }
        cout << endl;
    }
    
  2. 循环链表环判断(快慢指针)

    考试出现情况:2023年6月 编程题1第2题、2023年12月 编程题1第1题、2024年6月 编程题1第1题

    #include <iostream>
    using namespace std;
    
    struct ListNode {
        int val;
        ListNode* next;
        ListNode(int x) : val(x), next(nullptr) {}
    };
    
    bool has_cycle(ListNode* head) {
        if (!head || !head->next) return false;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while (fast && fast->next) {
            if (slow == fast) return true;
            slow = slow->next;
            fast = fast->next->next;
        }
        return false;
    }
    

(四)排序/查找类(基础必考)

  1. 二分查找(迭代版,避免溢出)

    考试出现情况:2023年12月 编程题1第2题、2023年9月 编程题1第1题、2024年9月 编程题1第1题

    #include <vector>
    using namespace std;
    
    int binary_search(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        while (left <= right) {
            int mid = left + (right - left) / 2; // 避免溢出
            if (nums[mid] == target) return mid;
            else if (nums[mid] < target) left = mid + 1;
            else right = mid - 1;
        }
        return -1;
    }
    
  2. 归并排序(稳定排序,O(n log n))

    考试出现情况:2023年3月 编程题1第1题、2023年12月 编程题1第2题、2024年12月 编程题1第1题

    #include <vector>
    using namespace std;
    
    void merge(vector<int>& arr, int left, int mid, int right) {
        int n1 = mid - left + 1, n2 = right - mid;
        vector<int> L(n1), R(n2);
        for (int i = 0; i < n1; ++i) L[i] = arr[left + i];
        for (int j = 0; j < n2; ++j) R[j] = arr[mid + 1 + j];
        int i = 0, j = 0, k = left;
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) arr[k++] = L[i++];
            else arr[k++] = R[j++];
        }
        while (i < n1) arr[k++] = L[i++];
        while (j < n2) arr[k++] = R[j++];
    }
    
    void merge_sort(vector<int>& arr, int left, int right) {
        if (left < right) {
            int mid = left + (right - left) / 2;
            merge_sort(arr, left, mid);
            merge_sort(arr, mid + 1, right);
            merge(arr, left, mid, right);
        }
    }
    
  3. 快速排序(优化版,随机基准)

    考试出现情况:2023年6月 编程题1第1题、2024年3月 编程题1第1题、2024年6月 编程题1第2题

    #include <vector>
    #include <algorithm>
    #include <cstdlib>
    #include <ctime>
    using namespace std;
    
    int partition(vector<int>& arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; ++j) {
            if (arr[j] <= pivot) {
                i++;
                swap(arr[i], arr[j]);
            }
        }
        swap(arr[i + 1], arr[high]);
        return i + 1;
    }
    
    int random_partition(vector<int>& arr, int low, int high) {
        srand(time(NULL));
        int rand_idx = low + rand() % (high - low + 1);
        swap(arr[rand_idx], arr[high]);
        return partition(arr, low, high);
    }
    
    void quick_sort(vector<int>& arr, int low, int high) {
        if (low < high) {
            int pi = random_partition(arr, low, high);
            quick_sort(arr, low, pi - 1);
            quick_sort(arr, pi + 1, high);
        }
    }
    

(五)贪心/分治/递归类(编程题2高频)

  1. 贪心-活动安排问题(最多不重叠活动)

    考试出现情况:2023年3月 编程题2第2题、2023年12月 编程题2第2题、2024年9月 编程题2第1题

    #include <vector>
    #include <algorithm>
    using namespace std;
    
    struct Activity {
        int start;
        int end;
    };
    
    bool cmp(Activity a, Activity b) {
        return a.end < b.end;
    }
    
    int max_activities(vector<Activity>& activities) {
        if (activities.empty()) return 0;
        sort(activities.begin(), activities.end(), cmp);
        int count = 1, last_end = activities[0].end;
        for (int i = 1; i < activities.size(); ++i) {
            if (activities[i].start >= last_end) {
                count++;
                last_end = activities[i].end;
            }
        }
        return count;
    }
    
  2. 分治-最大子数组和

    考试出现情况:2023年6月 编程题2第2题、2024年3月 编程题2第2题、2024年12月 编程题2第2题

    #include <vector>
    #include <climits>
    #include <algorithm>
    using namespace std;
    
    int cross_max(vector<int>& nums, int left, int mid, int right) {
        int left_max = INT_MIN, sum = 0;
        for (int i = mid; i >= left; --i) {
            sum += nums[i];
            left_max = max(left_max, sum);
        }
        int right_max = INT_MIN; sum = 0;
        for (int i = mid + 1; i <= right; ++i) {
            sum += nums[i];
            right_max = max(right_max, sum);
        }
        return left_max + right_max;
    }
    
    int max_subarray(vector<int>& nums, int left, int right) {
        if (left == right) return nums[left];
        int mid = left + (right - left) / 2;
        int left_max = max_subarray(nums, left, mid);
        int right_max = max_subarray(nums, mid + 1, right);
        int cross = cross_max(nums, left, mid, right);
        return max({left_max, right_max, cross});
    }
    
    int max_subarray_sum(vector<int>& nums) {
        if (nums.empty()) return 0;
        return max_subarray(nums, 0, nums.size() - 1);
    }
    
  3. 递归-斐波那契(记忆化优化)

    考试出现情况:2023年12月 编程题1第1题、2023年9月 编程题1第2题、2024年6月 编程题1第1题

    #include <vector>
    using namespace std;
    
    long long fib_memo(int n, vector<long long>& memo) {
        if (n <= 1) return n;
        if (memo[n] != -1) return memo[n];
        memo[n] = fib_memo(n - 1, memo) + fib_memo(n - 2, memo);
        return memo[n];
    }
    
    long long fibonacci(int n) {
        if (n < 0) return 0;
        vector<long long> memo(n + 1, -1);
        return fib_memo(n, memo);
    }
    
posted @ 2025-12-21 09:08  kkman2000  阅读(1)  评论(0)    收藏  举报