作者: fangys

  • 常用自定义函数总结

    交换函数

    #include <iostream>
    using namespace std;
    
    // 1. 值传递:无法交换外部变量
    void swap_value(int a, int b) {
        int temp = a;
        a = b;
        b = temp;
        // 函数内交换了形参,但对外部实参无影响
    }
    
    // 2. 引用传递:可以直接交换外部变量
    void swap_reference(int &a, int &b) {
        int temp = a;
        a = b;
        b = temp;
    }
    
    // 3. 地址传递:通过指针交换外部变量
    void swap_pointer(int *a, int *b) {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    
    int main() {
        int x = 5, y = 10;
        // 值传递
        swap_value(x, y);
        // 引用传递
        swap_reference(x, y);
        // 地址传递
        swap_pointer(&x, &y);
        return 0;
    }

    进制转换

    十进制转二进制

    string decToBin(int n) {
        if (n == 0) return "0";
        string res = "";
        while (n > 0) {
            res += (n % 2) + '0';
            n /= 2;
        }
        reverse(res.begin(), res.end());
        return res;
    }

    二进制转十进制

    int binToDec(string bin) {
        int res = 0;
        for (char ch : bin) {
            res = res * 2 + (ch - '0');
        }
        return res;
    }
    int binToDec(string bin) {
        int res = 0;
        int base = 1;                     // 2^0
        for (int i = bin.length() - 1; i >= 0; i--) {
            if (bin[i] == '1')
                res += base;
            base *= 2;                    // 下一位权重乘2
        }
        return res;
    }

    十进制转R进制

    string decToBase(int n, int R) {
        if (n == 0) return "0";
        string res = "";
        bool negative = false;
        if (n < 0) {
            negative = true;
            n = -n;
        }
        while (n > 0) {
            int remainder = n % R;
            if (remainder < 10) {
                res += remainder + '0';
            } else {
                res += (remainder - 10) + 'A';
            }
            n /= R;
        }
        if (negative) res += '-';
        reverse(res.begin(), res.end());
        return res;
    }

    R进制转十进制

    int baseToDec(string s, int R) {
        int res = 0;
        int start = 0;
        bool negative = false;
        string str = s;
        if (str[0] == '-') {
            negative = true;
            start = 1;
        }
        for (int i = start; i < str.length(); i++) {
            char ch = toupper(str[i]);
            int digit;
            if (ch >= '0' && ch <= '9') {
                digit = ch - '0';
            } else {
                digit = ch - 'A' + 10;
            }
            res = res * R + digit;
        }
        return negative ? -res : res;
    }

    数据处理

    判断质数(素数)

    // 基础版
    bool isPrime(int n) {
        if (n <= 1) return false;
        for (int i = 2; i * i <= n; i++) {
            if (n % i == 0) return false;
        }
        return true;
    }
    // 优化版
    bool isPrime1(int n) {
        if (n <= 1) return false;
        if (n == 2) return true;
        if (n % 2 == 0) return false;
        for (int i = 3; i * i <= n; i += 2) {
            if (n % i == 0) return false;
        }
        return true;
    }
    // 更优版
    bool isPrime2(int n) {
        if (n <= 1) return false;
        if (n <= 3) return true;   // 2,3 是质数
        if (n % 2 == 0 || n % 3 == 0) return false;
        // 所有质数都可表示为 6k±1
        for (int i = 5; i * i <= n; i += 6) {
            if (n % i == 0 || n % (i + 2) == 0) return false;
        }
        return true;
    }

    判断回文数

    // 优化版
    bool isPalindrome(const string& s) {
        int left = 0, right = s.length() - 1;
        while (left < right) {
            if (s[left] != s[right]) return false;
            left++;
            right--;
        }
        return true;
    }
    // 简易版
    bool isPalindrome(string s) {
        string rev = s;
        reverse(rev.begin(), rev.end());
        return s == rev;
    }
    // 数字版
    bool isPalindrome(int n) {
        // 负数直接不是回文(若认为-121不是回文,通常按题意)
        if (n < 0) return false;
        int original = n;
        int reversed = 0;
        while (n > 0) {
            reversed = reversed * 10 + n % 10;
            n /= 10;
        }
        return original == reversed;
    }

    数字倒序

    实际处理过程中,还需要注意数据正负以及前导零情况,灵活运用字符串与整数之间的转换函数以及reverse反转函数

    // 字符串倒序
    string reverseString(string s) {
        string res = s;
        int left = 0, right = res.length() - 1;
        while (left < right) {
            swap(res[left], res[right]);
            left++;
            right--;
        }
        return res;
    }
    // 数字倒序
    int reverseNumber(int n) {
        bool negative = false;
        if (n < 0) {
            negative = true;
            n = -n;
        }
        int reversed = 0;
        while (n > 0) {
            reversed = reversed * 10 + n % 10;
            n /= 10;
        }
        return negative ? -reversed : reversed;
    }
  • 自定义函数深入

    1. 函数的声明与定义

    • 声明(Declaration):告诉编译器函数的名字、返回类型、参数列表。不包含函数体。
    • 定义(Definition):给出函数的具体实现(函数体)。
    #include <iostream>
    using namespace std;
    
    // 函数声明(原型)
    int max(int a, int b);
    
    // 主函数
    int main() {
        cout << max(10, 20) << endl;  // 调用
        return 0;
    }
    
    // 函数定义
    int max(int a, int b) {
        return (a > b) ? a : b;
    }

    注意:如果定义在调用之前,可以省略单独声明。


    2. 函数的形参与实参

    • 形参(Parameter):定义函数时括号内的变量,用于接收数据。
    • 实参(Argument):调用函数时传给函数的具体值或变量。
    void printSum(int x, int y) {   // x, y 是形参
        cout << x + y << endl;
    }
    
    int main() {
        int a = 5, b = 3;
        printSum(a, b);  // a, b 是实参
        return 0;
    }

    实参会把自己的值拷贝给形参(取决于传递方式,见下节)。


    3. 函数的值传递、引用传递、地址传递

    3.1 值传递(Pass by Value)

    • 将实参的值拷贝一份给形参。
    • 函数内部修改形参不影响实参。
    void changeVal(int n) {
        n = 100;  // 只修改了拷贝
    }
    
    int main() {
        int a = 5;
        changeVal(a);
        cout << a;  // 输出 5,未改变
        return 0;
    }

    3.2 引用传递(Pass by Reference)

    • 形参是实参的别名,操作形参即操作实参本身。
    • 使用 & 声明。
    void changeRef(int &n) {
        n = 100;   // 直接修改原变量
    }
    
    int main() {
        int a = 5;
        changeRef(a);
        cout << a;  // 输出 100
        return 0;
    }

    优点:避免拷贝(尤其适合大对象),可直接修改实参。

    3.3 地址传递(Pass by Pointer)

    • 形参为指针,接收实参的地址。
    • 通过解引用操作实参。
    void changePtr(int *n) {
        *n = 100;   // 通过地址修改原变量
    }
    
    int main() {
        int a = 5;
        changePtr(&a);  // 传地址
        cout << a;      // 输出 100
        return 0;
    }

    注意:地址传递本质上也是值传递(拷贝了指针的值),但通过该地址可访问原变量。

    三种传递方式对比表

    传递方式是否修改实参传递内容适合场景
    值传递值的拷贝基本类型、小对象
    引用传递引用(别名)需修改实参、大对象
    地址传递地址(指针)需要指针语义或可为空

    4. 函数的作用域

    4.1 局部作用域

    • 函数内部定义的变量(包括形参)只能在函数内访问。
    • 函数结束后自动销毁。
    void func() {
        int localVar = 10;  // 局部变量
        cout << localVar;   // ✅ 可访问
    }
    
    int main() {
        // cout << localVar;  // ❌ 错误,不可访问
        return 0;
    }

    4.2 全局作用域

    • 定义在所有函数之外的变量,可在函数内直接访问。
    • 若局部变量与全局变量同名,局部会覆盖全局(可使用 :: 访问全局)。
    int global = 100;  // 全局变量
    
    void test() {
        int global = 200;        // 局部覆盖
        cout << global << endl;  // 输出 200
        cout << ::global << endl; // 输出 100(:: 访问全局)
    }

    4.3 静态局部变量(static)

    • 在函数内用 static 修饰,只初始化一次,函数结束后不销毁,下次调用保留上次的值。
    void counter() {
        static int count = 0;  // 只初始化一次
        count++;
        cout << "调用次数: " << count << endl;
    }
    
    int main() {
        counter();  // 调用次数: 1
        counter();  // 调用次数: 2
        return 0;
    }
  • 1151:素数个数

    #include <iostream>
    #include <cmath>
    using namespace std;
    double jiafa(double a,double b){
        return a+b;
    }
    //题目 输入一个数字,输出他是不是质数
    //函数:判断一个数字是不是质数 true false 1 0
    bool isprime(int n){
        if(n<2) return false;
        for(int i=2;i<=sqrt(n);i++)
            if(n%i==0) return false;
        return true;
    }
    int isprime2(int n){
        if(n<2) return 0;
        for(int i=2;i<n;i++){
            if(n%i==0) return 0;
        }
        return 1;
    }
    int main()
    {
        int n;
        cin>>n;
        int s=0;
        for(int i=2;i<=n;i++){
            if(isprime(i)){
                s++;
            }
        }
        cout<<s;
        return 0;
    }
  • 1152:最大数max(x,y,z)

    #include <bits/stdc++.h>
    using namespace std;
    
    double max3(double a,double b,double c){
        if(a>=b&&a>=c) return a;
        else{
            if(b>=c) return b;
            else return c;
        }
    }
    int main()//程序的主入口 主函数
    {
        double a,b,c;
        cin>>a>>b>>c;
        double m=max3(a,b,c)/(max3(a+b,b,c)*max3(a,b,b+c));
        cout<<fixed<<setprecision(3)<<m;
        return 0;
    }
  • 1155:回文三位数

    #include <bits/stdc++.h>
    using namespace std;
    bool isprime(int n){
        for(int i=2;i<=sqrt(n);i++)
            if(n%i==0) return false;
        return true;
    }
    int main()
    {
        for(int i=1;i<10;i+=2){
            for(int j=0;j<10;j++){
                int num=i*101+j*10;
                if(isprime(num))
                    cout<<num<<endl;
            }
        }
        return 0;
    }
    #include <bits/stdc++.h>
    
    using namespace std;
    int ss(int n){
        for(int i=2;i<=sqrt(n);i++){
            if(n%i==0) return 0;
        }
        return 1;
    }
    
    //函数:将一个数字反转成另一个数字
    int fanzhuan(int n){//比较死板,只能用于三位数,不通用
        int g=n%10;
        int s=n/10%10;
        int b=n/100;
        return g*100+s*10+b;
    }
    int fanzhuan2(int n){//123456789 987654321
        int m=0;
        while(n>0){
            m=m*10+n%10;
            n/=10;
        }
        return m;
    }
    int main()
    {
        for(int i=100;i<=999;i++){
            if(i==fanzhuan2(i)&&ss(i)) cout<<i<<endl;
        }
        return 0;
    }
  • 1154:亲和数

    #include <bits/stdc++.h>
    using namespace std;
    //求一个数字的因子之和
    int getysh(int n){
        int sum=0;
        for(int i=1;i<n;i++){
            if(n%i==0) sum+=i;
        }
        return sum;
    }
    int main(){
        int a=2;
        while(1){
            int b=getysh(a);
            int c=getysh(b);
            if(a<b&&a==c) {cout<<a<<" "<<b;return 0;}
            else a++;
        }
        return 0;
    }
  • 1007:计算(a+b)×c的值

    #include <iostream>
    using namespace std;
    int main(){
    	int a,b,c;
    	cin>>a>>b>>c;
    	cout<<(a+b)*c; 
    	return 0;
    }
  • 1003:对齐输出

    #include <iostream>
    #include <iomanip>
    using namespace std;
    
    int main(){
    	int a,b,c;
    	cin>>a>>b>>c;
    	cout<<setw(8)<<a<<" "<<setw(8)<<b<<" "<<setw(8)<<c;
    	return 0;
    }
  • 1002:输出第二个整数

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int a,b,c;
        cin>>a>>b>>c;
        cout<<b;
        return 0;
    }
  • 2062:【例1.3】电影票

    #include <iostream>
    using namespace std;
    
    int main()
    {
        int x;
        cin>>x;
        cout<<x<<" "<<x*10;
        return 0;
    }
  • CSS:入门及选择器

     CSS定义

    • CSS = Cascading Style Sheets(层叠样式表)
    • 作用:控制网页的外观(颜色、大小、位置、背景等)

    引入方式

    方式写法使用场景优缺点
    行内样式<h1 style="color:red;">极少单独修改某元素不推荐,结构和样式混在一起
    内部样式<style> 标签放在<head>单页面小案例基础常用
    外部样式<link rel="stylesheet" href="style.css">正式网站(多页面共用)最推荐,维护方便

    基本语法

    选择器 {
        属性: 值;
        属性2: 值2;
    }
    • 注释:/* 这是注释 */

    选择器

    元素选择器(标签选择器)

    • 选中页面中所有该标签
    • 示例:p { font-size: 16px; } → 所有段落文字16像素

    类选择器

    • 写法:.类名 { }
    • 需要在HTML标签上添加class="类名"
    • 优势:同一类样式可复用
    <style>
    .highlight {
        background-color: yellow;
        font-weight: bold;
    }
    </style>
    <p class="highlight">重要内容</p>

    ID选择器

    • 写法:#id名 { }
    • 唯一性:一个页面中同一个ID只能出现一次
    • 示例:#nav { background: black; }

    通配符选择器

    • 写法:* { }
    • 选中页面上所有元素(通常用于重置内外边距)
    • 示例:* { margin: 0; padding: 0; }

    选择器列表(分组)

    • 一次选中多个选择器,用逗号隔开
    • 示例:h1, h2, .title { color: green; }

    文字文本样式

    1. 字体样式(font家族)

    属性作用常用值
    font-family字体类型"微软雅黑", Arial, sans-serif(备选字体)
    font-size字体大小16px1.2em1.2rem(先掌握px和em)
    font-weight粗细normalbold100~900
    font-style倾斜normalitalic

    2. 颜色(color)

    • 三种写法:
      • 英文名:redbluelightgray
      • 十六进制:#ff6600(橙色)
      • rgb函数:rgb(100, 150, 200)

    3. 文本对齐(text-align)

    • 值:leftcenterrightjustify

    4. 行高(line-height)

    • 控制行间距,常用1.5(无单位,表示字体大小的1.5倍)

    5. 文字装饰(text-decoration)

    • 常用值:underline(下划线)、overline(上划线)、line-through(删除线)、none(无装饰)
    • 重点应用:去掉链接默认的下划线 a { text-decoration: none; }
    a:hover {
        color: #d35400;
        text-decoration: underline;
    } /* 鼠标移动到链接上时,更改颜色并显示下划线 */

  • 高精度类(面向对象)

    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <cassert>
    
    class BigInt {
    private:
        std::vector<int> digits;  // 小端序存储,digits[0] 是个位
        bool negative;            // true 表示负数,false 表示非负数
    
        // 去除前导零(同时处理结果为 0 时的符号)
        void trim() {
            while (digits.size() > 1 && digits.back() == 0)
                digits.pop_back();
            if (digits.size() == 1 && digits[0] == 0)
                negative = false;
        }
    
        // 比较绝对值大小(小端序),返回 1: |a|>|b|, 0: 相等, -1: |a|<|b|
        static int absCompare(const BigInt& a, const BigInt& b) {
            if (a.digits.size() != b.digits.size())
                return a.digits.size() > b.digits.size() ? 1 : -1;
            for (int i = a.digits.size() - 1; i >= 0; --i) {
                if (a.digits[i] != b.digits[i])
                    return a.digits[i] > b.digits[i] ? 1 : -1;
            }
            return 0;
        }
    
        // 加法核心(绝对值相加),结果为正
        static BigInt absAdd(const BigInt& a, const BigInt& b) {
            BigInt res;
            res.digits.clear();
            int carry = 0;
            size_t i = 0;
            while (i < a.digits.size() || i < b.digits.size() || carry) {
                int sum = carry;
                if (i < a.digits.size()) sum += a.digits[i];
                if (i < b.digits.size()) sum += b.digits[i];
                res.digits.push_back(sum % 10);
                carry = sum / 10;
                ++i;
            }
            res.negative = false;
            return res;
        }
    
        // 减法核心(绝对值相减),要求 |a| >= |b|,结果为正
        static BigInt absSub(const BigInt& a, const BigInt& b) {
            BigInt res;
            res.digits.clear();
            int borrow = 0;
            for (size_t i = 0; i < a.digits.size(); ++i) {
                int diff = a.digits[i] - borrow;
                if (i < b.digits.size()) diff -= b.digits[i];
                if (diff < 0) {
                    diff += 10;
                    borrow = 1;
                } else {
                    borrow = 0;
                }
                res.digits.push_back(diff);
            }
            res.negative = false;
            res.trim();
            return res;
        }
    
    public:
        // 构造函数
        BigInt() : digits{0}, negative(false) {}
        BigInt(long long num) : negative(false) {
            if (num < 0) {
                negative = true;
                num = -num;
            }
            do {
                digits.push_back(num % 10);
                num /= 10;
            } while (num > 0);
        }
        BigInt(const std::string& s) {
            if (s.empty()) {
                digits.push_back(0);
                negative = false;
                return;
            }
            size_t start = 0;
            negative = (s[0] == '-');
            if (negative || s[0] == '+') start = 1;
            for (int i = s.size() - 1; i >= (int)start; --i) {
                digits.push_back(s[i] - '0');
            }
            trim();
        }
    
        // 转换为字符串
        std::string toString() const {
            if (digits.empty()) return "0";
            std::string res;
            if (negative) res += '-';
            for (int i = digits.size() - 1; i >= 0; --i)
                res += (digits[i] + '0');
            return res;
        }
    
        // 比较运算符
        bool operator==(const BigInt& other) const {
            return negative == other.negative && digits == other.digits;
        }
        bool operator!=(const BigInt& other) const { return !(*this == other); }
        bool operator<(const BigInt& other) const {
            if (negative != other.negative) return negative;
            if (negative) {
                // 两个负数,绝对值大的反而小
                int cmp = absCompare(*this, other);
                return cmp == 1;
            } else {
                int cmp = absCompare(*this, other);
                return cmp == -1;
            }
        }
        bool operator>(const BigInt& other) const { return other < *this; }
        bool operator<=(const BigInt& other) const { return !(*this > other); }
        bool operator>=(const BigInt& other) const { return !(*this < other); }
    
        // 加减法
        BigInt operator+(const BigInt& other) const {
            if (negative == other.negative) {
                // 同号相加:绝对值相加,符号不变
                BigInt res = absAdd(*this, other);
                res.negative = negative;
                return res;
            } else {
                // 异号相加:相当于绝对值相减,符号由绝对值大的决定
                int cmp = absCompare(*this, other);
                if (cmp == 0) return BigInt(0);
                BigInt res;
                if (cmp == 1) { // |this| > |other|
                    res = absSub(*this, other);
                    res.negative = negative; // 符号与绝对值大的相同
                } else {
                    res = absSub(other, *this);
                    res.negative = other.negative;
                }
                return res;
            }
        }
    
        BigInt operator-(const BigInt& other) const {
            BigInt negOther = other;
            negOther.negative = !negOther.negative;
            return *this + negOther;
        }
    
        BigInt operator-() const {
            BigInt res = *this;
            if (res.digits.size() != 1 || res.digits[0] != 0)
                res.negative = !res.negative;
            return res;
        }
    
        // 乘法(普通 O(n^2) 算法,可替换为 Karatsuba)
        BigInt operator*(const BigInt& other) const {
            BigInt res;
            res.digits.assign(digits.size() + other.digits.size(), 0);
            for (size_t i = 0; i < digits.size(); ++i) {
                int carry = 0;
                for (size_t j = 0; j < other.digits.size(); ++j) {
                    long long prod = (long long)digits[i] * other.digits[j] + res.digits[i + j] + carry;
                    res.digits[i + j] = prod % 10;
                    carry = prod / 10;
                }
                if (carry) {
                    res.digits[i + other.digits.size()] += carry;
                }
            }
            res.negative = negative ^ other.negative;
            res.trim();
            return res;
        }
    
        // 除法(高精除以高精,返回商和余数)
        friend std::pair<BigInt, BigInt> divmod(const BigInt& a, const BigInt& b);
        BigInt operator/(const BigInt& other) const {
            return divmod(*this, other).first;
        }
        BigInt operator%(const BigInt& other) const {
            return divmod(*this, other).second;
        }
    
        // 复合赋值运算符
        BigInt& operator+=(const BigInt& other) { *this = *this + other; return *this; }
        BigInt& operator-=(const BigInt& other) { *this = *this - other; return *this; }
        BigInt& operator*=(const BigInt& other) { *this = *this * other; return *this; }
        BigInt& operator/=(const BigInt& other) { *this = *this / other; return *this; }
        BigInt& operator%=(const BigInt& other) { *this = *this % other; return *this; }
    
        // 输入输出
        friend std::ostream& operator<<(std::ostream& os, const BigInt& bi) {
            os << bi.toString();
            return os;
        }
        friend std::istream& operator>>(std::istream& is, BigInt& bi) {
            std::string s;
            is >> s;
            bi = BigInt(s);
            return is;
        }
    };
    
    // 除法辅助函数(高精除以高精,使用竖式试商)
    std::pair<BigInt, BigInt> divmod(const BigInt& a, const BigInt& b) {
        if (b == BigInt(0)) throw std::runtime_error("Division by zero");
        if (a == BigInt(0)) return {BigInt(0), BigInt(0)};
        // 确定商的符号
        bool resNegative = a.negative ^ b.negative;
        // 取绝对值进行计算
        BigInt absA = a;
        absA.negative = false;
        BigInt absB = b;
        absB.negative = false;
    
        if (absA < absB) return {BigInt(0), a}; // 商为0,余数为a
    
        BigInt quotient;
        BigInt remainder;
        remainder.digits.clear();
    
        // 从被除数的高位开始逐位试商
        for (int i = absA.digits.size() - 1; i >= 0; --i) {
            // 将当前余数左移一位(乘以10),加上被除数的下一位
            remainder.digits.insert(remainder.digits.begin(), absA.digits[i]);
            remainder.trim();
            // 试商:在 0-9 之间找到最大的 q 使得 q * absB <= remainder
            int q = 0;
            int left = 0, right = 9;
            while (left <= right) {
                int mid = (left + right) / 2;
                BigInt prod = absB * BigInt(mid);
                if (prod <= remainder) {
                    q = mid;
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
            quotient.digits.push_back(q);
            BigInt prod = absB * BigInt(q);
            remainder = remainder - prod;
            remainder.trim();
        }
        // 此时 quotient.digits 是从高位到低位存储的,需要反转成小端序
        std::reverse(quotient.digits.begin(), quotient.digits.end());
        quotient.trim();
        quotient.negative = resNegative;
        remainder.negative = a.negative; // 余数符号与被除数一致
        return {quotient, remainder};
    }
    
    // 示例用法
    int main() {
        BigInt a("12345678901234567890");
        BigInt b("98765432109876543210");
        std::cout << "a = " << a << "\nb = " << b << std::endl;
        std::cout << "a + b = " << a + b << std::endl;
        std::cout << "a - b = " << a - b << std::endl;
        std::cout << "a * b = " << a * b << std::endl;
        std::cout << "a / b = " << a / b << std::endl;
        std::cout << "a % b = " << a % b << std::endl;
        return 0;
    }
  • 高精度乘法(Karatsuba)

    对于两个 n 位数的大整数,普通竖式乘法(逐位相乘再累加)的时间复杂度是 O(n²)。当 n 很大(比如上万位)时,平方级的开销会非常显著。

    Karatsuba 算法 利用分治思想,将时间复杂度降到 O(n^{log₂3}) ≈ O(n^{1.585}),是第一个突破 O(n²) 的大数乘法算法。

    核心思想

    设我们要计算两个 n 位数 X 和 Y 的乘积(假设 n 是 2 的幂,不足则补零)。

    将 X 和 Y 各分成两半:

    X = A * 10^{m} + B
    Y = C * 10^{m} + D

    其中 m = n/2,A、C 是高半部分,B、D 是低半部分。那么:

    X * Y = (A*10^m + B) * (C*10^m + D)
          = AC * 10^{2m} + (AD + BC) * 10^{m} + BD

    普通做法需要计算四次乘法:AC, AD, BC, BD。Karatsuba 观察到:

    AD + BC = (A+B)(C+D) - AC - BD

    这样只需计算 三次乘法

    1. AC
    2. BD
    3. (A+B)(C+D)

    然后用加减法组合出最终结果。递归地对这三个乘积继续使用 Karatsuba 方法,直到数字足够小(例如 32 位以内)改用直接乘法。

    算法步骤

    1. 输入两个大数 X 和 Y(字符串或小端序数组)。
    2. 若长度小于某阈值(如 32 或 64),直接用普通乘法返回。
    3. 将 X 和 Y 补齐到相同长度,且长度为 2 的幂(方便分半)。
    4. 取分割点 m = n/2,得到 A、B、C、D。
    5. 递归计算:
      • z0 = karatsuba(A, C)
      • z2 = karatsuba(B, D)
      • z1 = karatsuba(A+B, C+D) - z0 - z2
    6. 合并结果:z0 * 10^{2m} + z1 * 10^{m} + z2
    7. 处理进位并返回。

    代码实现

    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    #include <cmath>
    
    using namespace std;
    
    using BigInt = vector<int>;
    
    // 普通高精度乘法(O(n^2)),用于小规模递归基础情况
    BigInt mulBrute(const BigInt& a, const BigInt& b) {
        BigInt c(a.size() + b.size(), 0);
        for (size_t i = 0; i < a.size(); ++i) {
            long long carry = 0;
            for (size_t j = 0; j < b.size(); ++j) {
                long long cur = c[i + j] + (long long)a[i] * b[j] + carry;
                c[i + j] = cur % 10;
                carry = cur / 10;
            }
            if (carry) c[i + b.size()] += carry; // 注意可能超过一位,但进位最多一位
        }
        while (c.size() > 1 && c.back() == 0) c.pop_back();
        return c;
    }
    
    // 辅助函数:两个大数相加(小端序)
    BigInt add(const BigInt& a, const BigInt& b) {
        BigInt c;
        int carry = 0;
        size_t i = 0;
        while (i < a.size() || i < b.size() || carry) {
            int sum = carry;
            if (i < a.size()) sum += a[i];
            if (i < b.size()) sum += b[i];
            c.push_back(sum % 10);
            carry = sum / 10;
            ++i;
        }
        return c;
    }
    
    // 辅助函数:两个大数相减(要求 a >= b)
    BigInt sub(const BigInt& a, const BigInt& b) {
        BigInt c;
        int borrow = 0;
        for (size_t i = 0; i < a.size(); ++i) {
            int diff = a[i] - borrow;
            if (i < b.size()) diff -= b[i];
            if (diff < 0) {
                diff += 10;
                borrow = 1;
            } else {
                borrow = 0;
            }
            c.push_back(diff);
        }
        while (c.size() > 1 && c.back() == 0) c.pop_back();
        return c;
    }
    
    // 大数左移(乘以 10^k),即在低位补 k 个 0
    BigInt shiftLeft(const BigInt& a, int k) {
        if (a.size() == 1 && a[0] == 0) return a;
        BigInt res(k, 0); // 低位补零
        res.insert(res.end(), a.begin(), a.end());
        return res;
    }
    
    // Karatsuba 乘法
    BigInt karatsuba(const BigInt& a, const BigInt& b) {
        // 基础情况:使用普通乘法
        const int THRESHOLD = 64; // 经验值,可调节
        if (a.size() < THRESHOLD || b.size() < THRESHOLD) {
            return mulBrute(a, b);
        }
    
        // 确保两数长度一致,并取最大长度
        size_t n = max(a.size(), b.size());
        // 向上取整为偶数,方便分半(也可取 n/2 向上取整)
        if (n % 2 != 0) ++n;
        BigInt a2 = a, b2 = b;
        a2.resize(n, 0);
        b2.resize(n, 0);
    
        size_t m = n / 2; // 分割点(低位部分长度)
        // 分割: A = 高位部分 (a2[m..n-1]), B = 低位部分 (a2[0..m-1])
        BigInt lowA(a2.begin(), a2.begin() + m);
        BigInt highA(a2.begin() + m, a2.end());
        BigInt lowB(b2.begin(), b2.begin() + m);
        BigInt highB(b2.begin() + m, b2.end());
    
        // 递归计算三个乘积
        BigInt z0 = karatsuba(highA, highB);          // AC
        BigInt z2 = karatsuba(lowA, lowB);            // BD
        BigInt sumA = add(highA, lowA);               // A+B
        BigInt sumB = add(highB, lowB);               // C+D
        BigInt z1 = karatsuba(sumA, sumB);            // (A+B)(C+D)
        z1 = sub(z1, z0);
        z1 = sub(z1, z2);                             // (A+B)(C+D) - AC - BD
    
        // 组合结果: z0 * 10^{2m} + z1 * 10^{m} + z2
        BigInt result = add(z2, shiftLeft(z1, m));
        result = add(result, shiftLeft(z0, 2 * m));
    
        // 去除前导零
        while (result.size() > 1 && result.back() == 0)
            result.pop_back();
        return result;
    }
    
    // 字符串转小端序
    BigInt strToVec(const string& s) {
        BigInt v;
        for (int i = s.size() - 1; i >= 0; --i)
            v.push_back(s[i] - '0');
        return v;
    }
    
    // 输出小端序
    void printVec(const BigInt& v) {
        for (int i = v.size() - 1; i >= 0; --i)
            cout << v[i];
        cout << endl;
    }
    
    int main() {
        string s1, s2;
        cin >> s1 >> s2;
        BigInt a = strToVec(s1);
        BigInt b = strToVec(s2);
        BigInt c = karatsuba(a, b);
        printVec(c);
        return 0;
    }

    复杂度分析

    • 时间复杂度:递推式 T(n) = 3T(n/2) + O(n),解得 T(n) = O(n^{log₂3}) ≈ O(n^{1.585})
    • 空间复杂度:递归栈深度 O(log n),每层需要存储中间结果,总空间 O(n)。

  • 高精度除法(非重点)

    算法思路(竖式模拟)

    代码实现

    #include <iostream>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    // 比较两个正整数字符串的大小(已去除前导零)
    bool greaterOrEqual(const string& a, const string& b) {
        if (a.size() != b.size()) return a.size() > b.size();
        return a >= b;
    }
    
    // 高精度减法:a - b,要求 a >= b,返回结果字符串
    string subString(const string& a, const string& b) {
        string res;
        int borrow = 0;
        int i = a.size() - 1, j = b.size() - 1;
        while (i >= 0 || j >= 0) {
            int diff = (i >= 0 ? a[i] - '0' : 0) - borrow;
            if (j >= 0) diff -= (b[j] - '0');
            if (diff < 0) {
                diff += 10;
                borrow = 1;
            } else {
                borrow = 0;
            }
            res.push_back(diff + '0');
            i--; j--;
        }
        // 去除前导零
        while (res.size() > 1 && res.back() == '0') res.pop_back();
        reverse(res.begin(), res.end());
        return res;
    }
    
    // 高精度乘法:字符串 * 一位整数 (0-9)
    string mulString(const string& a, int d) {
        if (d == 0) return "0";
        string res;
        int carry = 0;
        for (int i = a.size() - 1; i >= 0; --i) {
            int prod = (a[i] - '0') * d + carry;
            res.push_back(prod % 10 + '0');
            carry = prod / 10;
        }
        while (carry) {
            res.push_back(carry % 10 + '0');
            carry /= 10;
        }
        reverse(res.begin(), res.end());
        return res;
    }
    
    // 高精度除法:a / b,返回商和余数(余数通过引用返回)
    string divBig(const string& a, const string& b, string& remainder) {
        if (b == "0") throw runtime_error("Division by zero");
        if (!greaterOrEqual(a, b)) {
            remainder = a;
            return "0";
        }
        string quotient;
        remainder = "";   // 当前余数(初始为空)
        // 遍历被除数的每一位
        for (char ch : a) {
            remainder.push_back(ch);           // 将下一位加入余数末尾
            // 去除余数的前导零(保留一个 '0')
            size_t pos = remainder.find_first_not_of('0');
            if (pos != string::npos) remainder = remainder.substr(pos);
            else remainder = "0";
    
            // 在当前余数下试商(0-9)
            int q = 0;
            string product;
            // 尝试从9到0,找到最大的 q 使得 q*b <= remainder
            for (int d = 9; d >= 0; --d) {
                product = mulString(b, d);
                if (greaterOrEqual(remainder, product)) {
                    q = d;
                    break;
                }
            }
            quotient.push_back(q + '0');
            // 减去 product
            remainder = subString(remainder, product);
        }
        // 去除商的前导零
        size_t pos = quotient.find_first_not_of('0');
        if (pos == string::npos) quotient = "0";
        else quotient = quotient.substr(pos);
        // 余数可能为空,则置 "0"
        if (remainder.empty()) remainder = "0";
        return quotient;
    }
    
    int main() {
        string a, b;
        cin >> a >> b;
        string rem;
        string q = divBig(a, b, rem);
        cout << "商: " << q << "\n余数: " << rem << endl;
        return 0;
    }
  • 高精度除法(高精除以低精)

    基本原理

    高精度除以低精度,模拟手工竖式除法。例如计算 12345 ÷ 6

          2 0 5 7  (商)
      ┌──────
    6 │ 1 2 3 4 5
         1 2
         ---
           0 3
             0
            ---
             3 4
             3 0
             ---
               4 5
               4 2
               ---
                 3  (余数)

    关键步骤:

    1. 从被除数的最高位开始,依次取一位或多位,直到不小于除数。
    2. 当前被除数部分除以除数,得到商的当前位,余数作为下一次的被除数部分。
    3. 重复直到所有位处理完。

    在计算机实现中,我们通常使用一个变量 remainder 来记录当前的余数(或称为“被除数部分”)。因为除数较小,我们可以用循环模拟。

    代码实现

    #include <iostream>
    #include <string>
    #include <algorithm>
    using namespace std;
    
    // 高精度除法:大数字符串 s 除以整数 b,返回商(字符串),余数通过引用返回
    string divString(const string& s, int b, int& remainder) {
        string quot;
        remainder = 0;
        for (char ch : s) {               // 从左到右(高位到低位)
            int cur = remainder * 10 + (ch - '0');
            quot.push_back(cur / b + '0');
            remainder = cur % b;
        }
        // 去掉前导零,但保留一个 '0'
        size_t pos = quot.find_first_not_of('0');
        if (pos == string::npos) return "0";
        return quot.substr(pos);
    }
    
    int main() {
        string s;
        int b;
        cin >> s >> b;
        int rem;
        string q = divString(s, b, rem);
        cout << "商: " << q << "\n余数: " << rem << endl;
        return 0;
    }
    • 除法string 正序存储(高位在左)最自然,代码几乎和手动计算一样。
    • 加减乘法:小端序数组更方便进位/借位。
  • 高精度乘法

    基本原理

    高精度乘高精度,仍然模拟手工竖式乘法的过程。例如计算 123 × 45

          1 2 3
    ×       4 5
    -----------
          6 1 5   (123 × 5)
    +   4 9 2     (123 × 4,向左偏移一位)
    -----------
        5 5 3 5

    在计算机中,我们使用数组(小端序)存储每一位,然后:

    1. 用一个双重循环,将第一个数的每一位乘以第二个数的每一位。
    2. 乘积的个位放在结果数组的对应位置(下标 i+j),十位及以上作为进位累加到更高位。
    3. 最后处理所有进位,并去除前导零。

    算法步骤

    1. 初始化结果数组 c,长度至少为 len(a)+len(b),所有元素为 0。
    2. 遍历 i 从 0 到 len(a)-1
      • 初始化进位 carry = 0
      • 遍历 j 从 0 到 len(b)-1
        • temp = c[i+j] + a[i] * b[j] + carry
        • c[i+j] = temp % 10
        • carry = temp / 10
      • 如果 carry > 0,继续向高位传递:c[i+len(b)] += carry,但这里需要循环处理进位(因为一次进位可能传播多位)。简便做法是在内层循环结束后,用 while 处理剩余进位。
    3. 去除结果数组末尾(高位)的多余零,但至少保留一位(结果为 0 时保留一个 0)。

    代码实现

    #include <iostream>
    #include <vector>
    #include <string>
    #include <algorithm>
    
    using namespace std;
    
    // 字符串转小端序 vector
    vector<int> strToVec(const string& s) {
        vector<int> v;
        for (int i = s.size() - 1; i >= 0; --i)
            v.push_back(s[i] - '0');
        return v;
    }
    
    // 去除前导零
    void trim(vector<int>& v) {
        while (v.size() > 1 && v.back() == 0)
            v.pop_back();
    }
    
    // 高精度 × 高精度
    vector<int> mulBig(const vector<int>& a, const vector<int>& b) {
        vector<int> c(a.size() + b.size(), 0); // 结果最多有 len(a)+len(b) 位
        for (size_t i = 0; i < a.size(); ++i) {
            int carry = 0;
            for (size_t j = 0; j < b.size(); ++j) {
                int temp = c[i + j] + a[i] * b[j] + carry;
                c[i + j] = temp % 10;
                carry = temp / 10;
            }
            // 处理剩余进位
            size_t idx = i + b.size();
            while (carry) {
                int temp = c[idx] + carry;
                c[idx] = temp % 10;
                carry = temp / 10;
                ++idx;
            }
        }
        trim(c);
        return c;
    }
    
    // 输出大数
    void printVec(const vector<int>& v) {
        for (int i = v.size() - 1; i >= 0; --i)
            cout << v[i];
        cout << endl;
    }
    
    int main() {
        string s1, s2;
        cin >> s1 >> s2;
        vector<int> a = strToVec(s1);
        vector<int> b = strToVec(s2);
        vector<int> result = mulBig(a, b);
        printVec(result);
        return 0;
    }
  • 高精度乘法(高精乘以低精)

    基本原理

    模仿手算竖式乘法,这里是一个多位数乘以一个一位数(常规整数可能有多位,但我们将它视为一个整体,不拆开)。

    计算 A × b(A 是大整数,b 是常规整数,且 b 非负,通常 b 较小):

    1. 从 A 的最低位(数组下标0)开始,逐位乘以 b,再加上上一位的进位。
    2. 当前位的乘积 = A[i] × b + carry,当前结果位 = (A[i] × b + carry) % 10,新的进位 = (A[i] × b + carry) / 10
    3. 处理完 A 的所有位后,如果进位不为0,需要继续向高位添加进位数字(可能不止一位,比如进位是123,要拆成[3,2,1]依次加入)。
    4. 最后去除结果数组前端的多余零(前导零)。

    代码实现

    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    // 字符串 → 小端序 vector
    vector<int> strToVec(const string& s) {
        vector<int> v;
        for (int i = s.size() - 1; i >= 0; --i)
            v.push_back(s[i] - '0');
        return v;
    }
    
    // 高精度 × 低精度 (b 是普通整数,如 int)
    vector<int> mul(const vector<int>& a, int b) {
        vector<int> c;
        int carry = 0;
        for (int i = 0; i < a.size() || carry; ++i) {
            if (i < a.size()) carry += a[i] * b;
            c.push_back(carry % 10);
            carry /= 10;
        }
        // 去除前导零(但结果为零时应保留一个0)
        while (c.size() > 1 && c.back() == 0) c.pop_back();
        return c;
    }
    
    // 输出小端序大数
    void printVec(const vector<int>& v) {
        for (int i = v.size() - 1; i >= 0; --i)
            cout << v[i];
        cout << endl;
    }
    
    int main() {
        string s;
        int b;
        cin >> s >> b;
        vector<int> a = strToVec(s);
        vector<int> result = mul(a, b);
        printVec(result);
        return 0;
    }
  • 高精度减法

    高精度减法与加法类似,模拟手算竖式减法的过程,核心是借位(borrow)的处理。以及小减大的处理。

    基本原理

    计算 A - B(假设 A ≥ B,避免出现负数):

    1. 从最低位(数组下标0)开始,逐位相减。
    2. 如果当前位 A[i] - borrow - B[i] 小于 0,就需要向高位借 1,当前位加 10,并标记借位 borrow = 1;否则借位为 0。
    3. 继续下一位,直到所有位处理完。
    4. 最后去除结果数组前端多余的零(前导零)。

    判断 A ≥ B

    • 先比较长度:位数多的数更大。
    • 长度相同,从最高位(数组尾部)往低位比较,遇到第一个不相等的位,谁的数字大谁就大。
    • 全部相等则两数相等。

    vector比较数据大小

    // 比较两个小端序大数:返回 true 当且仅当 a >= b
    bool greaterOrEqual(const vector<int>& a, const vector<int>& b) {
        if (a.size() != b.size()) return a.size() > b.size();
        for (int i = a.size() - 1; i >= 0; --i) {
            if (a[i] != b[i]) return a[i] > b[i];
        }
        return true; // 相等
    }

    string比较数据大小(注意前导零)

    // 比较两个字符串大数:返回 true 当且仅当 a >= b
    bool greaterOrEqual(const string& a, const string& b) {
        if (a.size() != b.size()) return a.size() > b.size();
        return a > b;
    }

    代码实现

    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    // 字符串 → 小端序 vector
    vector<int> strToVec(const string& s) {
        vector<int> v;
        for (int i = s.size() - 1; i >= 0; --i)
            v.push_back(s[i] - '0');
        return v;
    }
    
    // 比较两个小端序大数:返回 true 当且仅当 a >= b
    bool greaterOrEqual(const vector<int>& a, const vector<int>& b) {
        if (a.size() != b.size()) return a.size() > b.size();
        for (int i = a.size() - 1; i >= 0; --i) {
            if (a[i] != b[i]) return a[i] > b[i];
        }
        return true; // 相等
    }
    
    // 高精度减法,要求 a >= b
    vector<int> sub(const vector<int>& a, const vector<int>& b) {
        vector<int> c;
        int borrow = 0;  // 借位,值为 0 或 1
        for (int i = 0; i < a.size(); ++i) {
            int diff = a[i] - borrow;
            if (i < b.size()) diff -= b[i];
            if (diff < 0) {
                diff += 10;
                borrow = 1;
            } else {
                borrow = 0;
            }
            c.push_back(diff);
        }
        // 去除前导零(保留至少一位)
        while (c.size() > 1 && c.back() == 0) {
            c.pop_back();
        }
        return c;
    }
    
    // 输出小端序大数
    void printVec(const vector<int>& v) {
        for (int i = v.size() - 1; i >= 0; --i)
            cout << v[i];
        cout << endl;
    }
    
    int main() {
        string s1, s2;
        cin >> s1 >> s2;
        vector<int> a = strToVec(s1);
        vector<int> b = strToVec(s2);
    
        if (greaterOrEqual(a, b)) {
            vector<int> result = sub(a, b);
            printVec(result);
        } else {
            cout << "-";
            vector<int> result = sub(b, a);
            printVec(result);
        }
        return 0;
    }
  • 高精度加法

    高精度加法就是用来计算两个远远超过long long范围的大整数相加的方法。它的核心思想非常简单:模拟我们手算竖式加法

    加法步骤

    假设两个大数 A 和 B 已经转成上述的小端序数组。我们做加法:

    1. 从 i = 0 开始,依次处理每一位。
    2. 将 A[i] + B[i] + 进位值 相加,得到 sum
    3. 当前结果位 = sum % 10,新的进位 = sum / 10
    4. 继续处理下一位,直到两个数的所有位都处理完,并且进位为0为止。

    代码实现

    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    // 将字符串转为小端序 vector
    vector<int> strToVec(const string &s) {
        vector<int> v;
        for (int i = s.size() - 1; i >= 0; --i)
            v.push_back(s[i] - '0');
        return v;
    }
    
    // 高精度加法
    vector<int> add(const vector<int> &a, const vector<int> &b) {
        vector<int> c;
        int carry = 0;   // 进位
        int i = 0;
        // 只要还有数没加完或者还有进位,就继续
        while (i < a.size() || i < b.size() || carry) {
            int sum = carry;
            if (i < a.size()) sum += a[i];
            if (i < b.size()) sum += b[i];
            c.push_back(sum % 10);   // 当前位
            carry = sum / 10;        // 新的进位
            ++i;
        }
        return c;
    }
    
    // 输出大数(将小端序转为正常字符串输出)
    void printVec(const vector<int> &v) {
        for (int i = v.size() - 1; i >= 0; --i)
            cout << v[i];
        cout << endl;
    }
    
    int main() {
        string s1, s2;
        cin >> s1 >> s2;
        vector<int> a = strToVec(s1);
        vector<int> b = strToVec(s2);
        vector<int> result = add(a, b);
        printVec(result);
        return 0;
    }

    注意数据范围,是否存在负数或特殊符号,需要特殊处理。

  • 高精度算法入门

    高精度算法用于处理超出标准数据类型(如 intlong long)范围的大整数运算。常见应用包括大数加减乘除、阶乘、大数比较等。其核心思想是将大数以数组或字符串形式存储,每一位单独进行运算,并手动处理进位与借位。

    基本思想

    本质上就是用程序模拟我们列竖式计算的过程。

    存储方式

    通常采用小端序(低位在数组前端),例如数字 12345 存储为 [5,4,3,2,1],这样从第 0 位开始进位/借位非常方便。

    可以使用 vector<int> 或 string

    数据处理相关代码

    高精度字符串转数组

    // 将字符串表示的大数转换为小端序 vector(下标0存个位)
    vector<int> strToVec(const string& s) {
        vector<int> v;
        // 从字符串末尾(最低位)开始存储
        for (int i = s.size() - 1; i >= 0; i--) {
            v.push_back(s[i] - '0');
        }
        return v;
    }

    数组转高精度字符串

    // 如果需要返回字符串(便于后续使用)
    string vecToStr(const vector<int>& v) {
        string s;
        for (int i = v.size() - 1; i >= 0; i--) {
            s.push_back(v[i] + '0');
        }
        return s;
    }
  • 考级集训课:GESP八级

    CCF编程能力等级认证考试大纲

    编号知识块知识点
    1计数原理加法原理
    乘法原理
    2排列与组合排列
    组合
    3杨辉三角杨辉三角的定义
    杨辉三角形的实现
    4倍增法倍增的概念
    5代数与平面几何一元一次方程
    二元一次方程
    三角形、圆形、长方形面积
    6图论算法及应用最小生成树的概念、kruskal算法、prim算法
    最短路径的概念、dijkstra算法、Floyd算法
    图论算法的综合应用与问题求解技巧
    7算法时空效率分析算法时空复杂度的一般分析方法
    各类算法(排序、查找、树图遍历、搜索、分治、动归)的时空复杂度
    8算法优化不同算法求解的差异分析
    算法优化的一般方法
    根据数学知识优化算法的一般方法(包括不限于等差、等比数列的求和公式等)

    计数原理

    排列与组合

    杨辉三角

    倍增法

    代数与平面几何

    图论算法及应用

    算法时空效率分析

    算法优化

  • 1001:Hello,World!

    #include <iostream>
    using namespace std;
    int main()
    {
    	cout<<"Hello,World!";
    	return 0;
    }
  • 2063:【例1.4】牛吃牧草

    设草地中共有♥️堆草,每天还能生长♦️堆,一头牛每天吃♣️堆

    则:

    • 15头牛,20天一共吃:15*20♣️堆=♥️+20*♦️
    • 20头牛,10天一共吃:20*10♣️堆=♥️+10*♦️

    最总结果每天生长:♦️=10♣️

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        cout<<10;
        return 1;
    }
  • 自定义函数入门

    基本概念

    核心目的:避免重复造轮子,拒绝“复制粘贴”。

    • 代码复用:同样的功能(比如求两数最大值),不需要反复写相同的代码。
    • 模块化:将大问题拆解成小任务(像乐高积木一样)。
    • 易维护:如果需要修改某个功能,只需要改函数内部,其他地方调用它的不用动。

    基本结构:五要素

    返回值类型 函数名(参数1类型 参数1名, 参数2类型 参数2名) {
        // 函数体:具体执行的代码
        return 返回值; // 如果返回值类型是 void,这行可以省略
    }

    案例对比:无函数 vs 有函数

    需求没有函数(直接在 main 里写)有函数(封装逻辑)
    求两个数的和int a=3, b=5;
    int s = a+b;
    cout << s;
    int add(int x, int y){
    return x+y;
    }
    // 调用
    cout << add(3,5);
    优点分析虽然简单,但如果要求 100 次和,代码会很冗长一次编写,随处调用

    实际操作流程

    以求三个整数的最大值为例。

    1. 确定函数签名:
      • 返回值:最大值是数字(整数),所以需要用int表示返回值类型
      • 名字:见名知意,叫max3
      • 参数:三个整数,所以是int a,int b,int c
    2. 编写函数体:
    int max3(int a, int b, int c) {
        if (a >= b && a >= c) {
            return a;
        } 
        else {
            if (c > b) return c;
            else return b;
        }
    }
    1. 在main函数中调用
    #include <iostream>
    using namespace std;
    
    // 注意:函数通常写在 main 函数之前(或者先声明)
    int max3(int a, int b, int c) {
        if (a >= b && a >= c) {
            return a;
        } 
        else {
            if (c > b) return c;
            else return b;
        }
    }
    
    int main() {
        int x = 10, y = 20, z = 25;
        
        // 调用函数,将 x 传给 a,y 传给 b,z 传给 c
        int result = max3(x, y, z); 
        
        cout << "最大值是:" << result << endl; // 输出 25
        return 0;
    }

    形参与实参

    • 形式参数:定义函数时 (int a, int b, int c) 中的 a、b 和 c
      • 它们在函数没被调用时,不占用内存,也没有具体的值。
    • 实际参数:调用函数时 max3(x, y, z) 中的 x、y 和 z
      • 它们是 main 函数里真实存在的变量。

    无返回类型:void

    只做事情,不需要返回结果的函数,用void类型

    // 打印10个星号,不需要返回任何数
    void printStars() {
        for(int i = 0; i < 10; i++) {
            cout << "*";
        }
        // 这里没有 return 语句(或者可以写 return;)
    }
    
    int main() {
        printStars(); // 直接调用,不能赋值给变量
        return 0;
    }

    驼峰命名法

    核心规则:多个单词组成一个名称时,第一个单词首字母小写,后续每个单词首字母大写。因为大写字母像驼峰,所以得名。

    如:变量作用是学生名字,则通过student和name命名,根据驼峰命名法的规则,s小写,n大写,最终结果是studentName。

  • 1143:最长最短单词

    字符串处理

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	char c[20050];
    	int l,a=0,big=0,x,x2,small=9999;
    	cin.getline(c,20050);
    	l=strlen(c);
    	c[l]=' ';
    	c[l+1]='\0';
    	for(int i=0;i<=l;i++) if(c[i]==',') c[i]==' ';
    	for(int i=0;i<=l;i++){
    		if(c[i]!=' '&&c[i]!=',')
    			a++;
    		else {
    			if(a>big) {big=a;x=i;}
    			if(a<small) {small=a;x2=i;}
    			a=0;
    		}
    	}
    	for(int i=x-big;i<x;i++){
    		cout<<c[i];
    	}
    	cout<<endl;
    	for(int i=x2-small;i<x2;i++){
    		cout<<c[i];
    	}
    	return 0;
    }
  • 1139:整理药名

    模拟过程,字符串操作

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin >> n;
        string a[200];
        for(int i=0;i<n;i++){
            cin >> a[i];
        }
        for(int i=0;i<n;i++){
            if(a[i][0]>='a'&&a[i][0]<='z'){
                a[i][0]-=32;
            }  
            for(int j=1;j<a[i].size();j++){
                if(a[i][j]>='A'&&a[i][j]<='Z'){
                    a[i][j]+=32;
                }
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<a[i].size();j++){
                cout << a[i][j];
            }
            cout << endl;
        }
        return 0;
    }
  • 1132:石头剪子布

    模拟过程,字符串操作

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	char play1[50]={},play2[50]={};
    	int n;
    	cin>>n;
    	while(n--)//循环n次
    	{
    		cin>>play1>>play2;
    		if(play1[0]==play2[0])cout<<"Tie\n";
    		else if(play1[0]=='R'){
    			if(play2[0]=='S') cout<<"Player1\n";
    			else cout<<"Player2\n";
    		}
    		else if(play1[0]=='S'){
    			if(play2[0]=='P') cout<<"Player1\n";
    			else cout<<"Player2\n";
    		}
    		else {
    			if(play2[0]=='R') cout<<"Player1\n";
    			else cout<<"Player2\n";
    		}
    	}
    	return 0;
    }
  • 1131:基因相关性

    模拟过程,字符串操作

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	char a[550],b[550];
    	double d;
    	cin>>d>>a>>b;
    	int s=0;
    	for(int i=0;i<strlen(b);i++){
    		if(a[i]==b[i]) s++;
    	}
    	double d2=1.0*s/strlen(b);
    	if(d2>=d) cout<<"yes";
    	else cout<<"no";
    	return 0;
    }
  • 1130:找第一个只出现一次的字符

    模拟过程,字符串操作

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	char a[100050];
    	cin>>a;
    	for(int i=0;i<strlen(a);i++){
    		char c=a[i];
    		bool f=true;
    		for(int j=0;j<strlen(a);j++){
    			if(i!=j&&a[j]==c){
    				f=false;
    				break;
    			}
    		}
    		if(f){
    			cout<<c;
    			return 0;
    		}
    	}
    	cout<<"no";
    	return 0;
    }
  • 2050:【例5.20】字串包含

    灵活使用字符串相关函数处理问题

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	string a,b;
    	cin>>a>>b;
    	string aa=a+a,bb=b+b;
    	if(bb.find(a)<bb.size()||aa.find(b)<aa.size()) cout<<"true";
    	else cout<<"false";
    	return 0;
    }
    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    	string a,b;
    	cin>>a>>b;
    	string aa=a+a,bb=b+b;
        // 查看aa中是否包含b
        for(int i=0;i<aa.size();i++){
            if(aa.substr(i,b.size())==b){
                cout<<"true";
                return 0;
            }
        }
        // 查看bb中是否包含a
        for(int i=0;i<bb.size();i++){
            if(bb.substr(i,a.size())==a){
                cout<<"true";
                return 0;
            }
        }
        cout<<"false";
    	return 0;
    }
  • 2049:【例5.19】字符串判等

    考察字符串操作,大小写转换,去掉空格等

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        string s1,s2;
        getline(cin,s1);
        getline(cin,s2);
        string s3,s4;
        for(int i=0;i<s1.size();i++){
            if(s1[i]==' ') continue;
            if(s1[i]>'Z') s1[i]-=32;
            s3+=s1[i];
        }   
        for(int i=0;i<s2.size();i++){
            if(s2[i]==' ') continue;
            if(s2[i]>'Z') s2[i]-=32;
            s4+=s2[i];
        }   
        if(s3==s4) cout<<"YES";
        else cout<<"NO";
        return 0;
    }
  • 2048:【例5.18】串排序

    字符串+冒泡排序

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n;
        cin>>n;
        string s[n];
        for(int i=0;i<n;i++){
            cin>>s[i];
        }
        for(int i=0;i<n-1;i++){
            for(int j=0;j<n-i-1;j++){
                if(s[j]>s[j+1]) swap(s[j],s[j+1]);
            }
        }
        for(int i=0;i<n;i++){
            cout<<s[i]<<endl;
        }
        return 0;
    }
  • 2047:【例5.16】过滤空格

    核心:如果前一个是空格,当前还是空格,则当前的空格是多余的

    #include <iostream>
    using namespace std;
    int main()
    {
        string s;
        getline(cin,s);
        for(int i=0;i<s.size();i++){
            if(i!=0&&s[i]==' '&&s[i-1]==' ') continue;
            else cout<<s[i];
        }
        return 0;
    }
  • 2046:【例5.15】替换字母

    考察字符串、字符的输入输出以及遍历

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        char c[210];
        cin.getline(c,210);
        char a,b;
        cin>>a>>b;
        for(int i=0;i<strlen(c);i++){
            if(c[i]==a) cout<<b;
            else cout<<c[i];
        }
        return 0;
    }
    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
        string s;//字符串 长度没有限制
        getline(cin,s);//字符串的输入
        char a,b;
        cin>>a>>b;
        for(int i=0;i<s.size();i++){
            if(s[i]==a) cout<<b;
            else cout<<s[i];
        }
        return 0;
    }
  • 阶段性总结与提高

    输入输出

    cin、cout

    int a;
    float b;
    char c;
    char d[100];
    string e;
    cin >> a >> b >> c >> d >> e;
    cout << a << b << c << d << e;

    带空格字符数组、字符串输入

    char c[100];
    cin.ignore(); // 清除之前的换行符
    cin.getline(c, 100);
    
    string s;
    cin.ignore(); // 清除之前的换行符
    getline(cin, s);

    scanf()、printf()

    int a;
    float b;
    long long c;
    double d;
    char e;
    char f[100];
        
    scanf("%d %f %lld %lf %c %s", &a, &b, &c, &d, &e, f);
    printf("%d %f %lld %lf %c %s", a, b, c, d, e, f);

    printf和scanf不支持string类型的操作,但可以转换输出

    string s="hello world";
    printf("%s",s.c_str());

    getchar()、putchar()

    char ch;
    ch = getchar();  // 从键盘读取一个字符
    putchar(ch);     // 输出一个字符

    getline、cin.getline、getchar均需要注意缓冲区换行符问题

    格式化对比

    功能printfcout
    换行"\n"endl 或 "\n"
    整数%d直接输出
    小数%f直接输出
    保留2位小数%.2fcout << fixed << setprecision(2)
    宽度5位%5dcout << setw(5)
    左对齐%-5dcout << left << setw(5)
    右对齐%5dcout << right << setw(5)
    填充0%05dcout << setfill('0') << setw(5)

    时间超时处理

    当输入或输出的次数达到10万以上时,超时(TLE)概率极高,因此需要特殊处理,以下两个方案均可:

    1. 使用scanf和printf代替cin和cout(推荐)
    2. cin/cout加速(必会)
    #include <iostream>
    using namespace std;
    
    int main() {
        // 加速代码(固定写法)
        ios::sync_with_stdio(false);
        cin.tie(0);
        cout.tie(0);
        
        return 0;
    }

    使用cin/cout加速后,endl用”\n”代替,且不能与scanf与printf混用。

    此外,最优方案为自定义快读快写(暂时不需要掌握,达到100w以上才需要)

    运算符

    算术运算符

    运算符含义示例
    +加法a + b
    -减法a - b
    *乘法a * b
    /除法(整数相赢得整数)10 / 3 = 3
    %取模(余数)10 % 3 = 1
    ++自增i++ (后置), ++i (前置)
    --自减i----i

    赋值运算符

    运算符示例等价于
    =a = 5
    +=a += 3a = a + 3
    -=a -= 3a = a - 3
    *=a *= 3a = a * 3
    /=a /= 3a = a / 3
    %=a %= 3a = a % 3

    关系运算符(比较运算符)

    运算符含义示例
    ==等于a == b
    !=不等于a != b
    >大于a > b
    <小于a < b
    >=大于等于a >= b
    <=小于等于a <= b

    逻辑运算符

    运算符含义说明
    &&逻辑与两者都为真才真
    ||逻辑或一个为真即为真
    !逻辑非取反

    位运算符

    运算符含义示例
    &按位与5 & 3 = 1 (101 & 011 = 001)
    |按位或5 | 3 = 7 (101 | 011 = 111)
    ^按位异或5 ^ 3 = 6 (101 ^ 011 = 110)
    ~按位取反~5 = -6
    <<左移5 << 1 = 10 (1010)
    >>右移5 >> 1 = 2 (10)

    条件运算符(三目、三元)

    // 语法: 条件 ? 真值 : 假值
    int a = 10, b = 20;
    int max = (a > b) ? a : b;  // max = 20

    其他运算符

    sizeof运算符

    int a = 10;
    cout << sizeof(int) << endl;   // 4 字节
    cout << sizeof(a) << endl;     // 4
    cout << sizeof(double) << endl; // 8

    逗号运算符

    int a, b, c;
    a = (b=3, c=5, b+c);  // 从左到右执行,返回最后一个值
    cout << a << endl;  // 8

    优先级

    优先级运算符结合性
    1() [] -> . ::左到右
    2! ~ ++ -- + - * & (单目)右到左
    3* / %左到右
    4+ -左到右
    5<< >>左到右
    6< <= > >=左到右
    7== !=左到右
    8&左到右
    9^左到右
    10|左到右
    11&&左到右
    12||左到右
    13?:右到左
    14= += -= 等右到左
    15,左到右
  • 算法复杂度概念梳理


    一、基本概念

    1. 大O表示法

    • 表示最坏情况下的增长率(忽略常数、低阶项)
    • 例:( T(n)=3n2+2n+1)(O(n2)T(n) = 3n^2 + 2n + 1 ) → ( O(n^2) )

    2. 常见复杂度(从快到慢)

    • O(1)<O(logn)<O(n)<O(nlogn)<O(n2)<O(2n)<O(n!)O(1) < O(\log n) < O(n) < O(n\log n) < O(n^2) < O(2^n) < O(n!)

    二、多项式复杂度

    1. (O(1) O(1) ) —— 常数时间

    特征:执行次数与输入规模 n 无关

    代码结构

    int a = arr[0];           // 一次操作
    int b = a * 2 + 5;        // 几次运算,仍 O(1)

    2. (O(n) O(n)) —— 线性时间

    特征:单层循环,循环次数与 n 成正比

    代码结构

    for(int i = 0; i < n; i++) {
        // O(1) 操作
    }

    3. (O(n2)O(n^2) ) —— 平方时间

    特征:双重循环,内外都与 n 相关

    代码结构

    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            // O(1) 操作
        }
    }

    变体(仍是 (O(n2)O(n^2))):

    for(int i = 0; i < n; i++) {
        for(int j = i; j < n; j++) {  // 约 n²/2 次
            // ...
        }
    }

    4. (O(n3) O(n^3) ) —— 立方时间

    特征:三重循环

    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            for(int k = 0; k < n; k++)
                // O(1)

    5. 多项式复杂度的加法 vs 乘法

    • 顺序执行:(O(n)+O(n2)=O(n2)O(n) + O(n^2) = O(n^2) )(取最高阶)
    • 嵌套执行:(O(n)×O(n)=O(n2)O(n) \times O(n) = O(n^2))

    三、对数复杂度 (O(logn) O(\log n) )

    特征

    • 每次循环将问题规模减半(或变为固定比例)
    • 常见于二分法、倍增法

    代码结构 1:循环除以 2

    int i = n;
    while(i > 0) {
        // O(1) 操作
        i /= 2;          // 关键:每次减半
    }

    分析:n → n/2 → n/4 → … → 1,共 (log2n \log_2 n ) 步

    代码结构 2:循环乘以 2

    for(int i = 1; i < n; i *= 2) {
        // O(1) 操作
    }

    分析:1, 2, 4, 8, …, 共 (log2n \log_2 n) 步

    代码结构 3:二分查找

    int l = 0, r = n-1;
    while(l <= r) {
        int mid = (l+r)/2;
        if(arr[mid] == target) return mid;
        else if(arr[mid] < target) l = mid+1;
        else r = mid-1;
    }

    四、(O(nlogn) O(n \log n) ) 复杂度

    特征

    • 外层线性,内层对数
    • 或分治算法(归并排序、快速排序的期望)

    代码结构 1:n × log n

    for(int i = 0; i < n; i++) {      // O(n)
        int j = n;
        while(j > 0) {                // O(log n)
            // O(1)
            j /= 2;
        }
    }

    代码结构 2:埃氏筛

    bool isPrime[n+1];
    fill(isPrime, isPrime+n+1, true);
    for(int i = 2; i <= sqrt(n); i++) {     // 外层 ~√n
        if(isPrime[i]) {
            for(int j = i*i; j <= n; j += i) {  // 内层总次数 ~ n log log n
                isPrime[j] = false;
            }
        }
    }

    复杂度:( O(nloglogn)O(n \log \log n) ) —— 属于 ( O(nlogn)O(n \log n) ) 级别

    代码结构 3:归并排序

    每次分两半,深度 (logn \log n ),每层合并 ( O(n)O(n) ),总 ( O(nlogn)O(n \log n) )


    五、指数复杂度 ( O(2n)O(2^n) )

    特征

    • 每层递归分成两个子问题
    • 常见于暴力搜索、递归枚举子集

    代码结构:递归枚举子集

    void dfs(int idx, int n) {
        if(idx == n) return;
        // 选或不选两种选择
        dfs(idx+1, n);   // 不选
        dfs(idx+1, n);   // 选
    }

    调用次数:(2n2^n )

    另一种:斐波那契递归(未优化)

    int fib(int n) {
        if(n <= 1) return n;
        return fib(n-1) + fib(n-2);  // 两个递归调用
    }

    复杂度 ( O(2n)O(2^n) )(实际约 ( ϕn\phi^n ),以1.618为底)

  • 初等数论概念梳理

    一、概念与性质梳理

    1. 素数与合数

    • 素数:大于 1,只有 1 和自身两个正因数
    • 合数:大于 1,有除 1 和自身外的其他因数
    • 1 和 0:既非素数也非合数
    • 判定:试除法 ( O(n)O(\sqrt{n}) )

    2. 最大公约数(GCD)

    • 若干数公共因数中的最大值
    • 性质:( gcd(a,b)=gcd(b,amodb)\gcd(a, b) = \gcd(b, a\bmod b) )

    3. 最小公倍数(LCM)

    • ( lcm(a,b)=a×bgcd(a,b)\mathrm{lcm}(a, b) = \frac{a \times b}{\gcd(a, b)} )
    • 注意:防止溢出(先除后乘)

    4. 同余与模运算

    • ( ab(modm)a \equiv b \pmod{m} ) 表示 (m|ab m \mid a-b )
    • 运算性质:
    • 加法/乘法可分别取模
    • 没有除法(需用逆元,但五级一般仅考同余基本概念)

    5. 约数与倍数

    • ( d|nd \mid n ):( d ) 是 ( n ) 的约数
    • 通过质因数分解求约数个数、约数和

    6. 质因数分解

    • 唯一分解定理:( n=p1k1p2k2n = p_1^{k_1} p_2^{k_2} \dots )

    7. 奇偶性

    • 加减:奇+奇=偶;奇+偶=奇;奇-奇=偶
    • 乘:有偶则偶;全奇为奇

    二、核心算法

    1. 辗转相除法(欧几里得算法)

    gcd(a, b):
        while b != 0:
            a, b = b, a % b
        return a

    复杂度:( O(logmin(a,b))O(\log \min(a,b)) )
    应用:求 GCD / LCM

    2. 唯一分解定理

    • 整数与质因数的双射关系
    • 应用:
      • 约数个数公式:d(n)=(k1+1)(k2+1)d(n) = (k_1+1)(k_2+1)\dots
      • 约数和公式:σ(n)=i(1+pi++piki)\sigma(n) = \prod_{i}(1 + p_i + \dots + p_i^{k_i})

    3. 埃氏筛(Eratosthenes)

    is_prime = [True] * (n+1)
    is_prime[0] = is_prime[1] = False
    for i in range(2, sqrt(n)+1):
        if is_prime[i]:
            for j in range(i*i, n+1, i):
                is_prime[j] = False

    复杂度:( O(nloglogn)O(n \log \log n) )
    特点:简单、空间适中、适合 n ≤ 1e7

    4. 线性筛(欧拉筛)

    primes = []
    is_prime = [True] * (n+1)
    for i in range(2, n+1):
        if is_prime[i]:
            primes.append(i)
        for p in primes:
            if i * p > n: break
            is_prime[i * p] = False
            if i % p == 0: break

    复杂度:( O(n)O(n) )
    特点:每个合数只被最小质因子筛一次,适合 n ≤ 1e7~1e8


    累加符号 ∑(Sigma)

    含义:把一系列数加起来。

    一般形式i=1nai=a1+a2++anai是通项公式

    i下标变量(从下界到上界,每次 +1)

    累乘符号 ∏(Product)

    含义:把一系列数乘起来。

    一般形式i=1nai=a1×a2××an

  • 算法与描述实战

    算法描述相关定义

    核心定义:算法是指一个被定义好的、计算机可实施的有限步骤或次序,用于解决问题

    五个基本特征

    特征含义真题角度
    输入项算法有0个或多个输入判断题:算法必须有输入?❌(可以有0个输入)
    输出项算法有1个或多个输出判断题:算法必须有输出?✅
    确定性每一步有确定定义,无二义性选择题:下列哪项不属于算法的特征?
    有穷性必须在有限步后终止判断题:算法可以无限循环?❌
    可行性每一步都能精确执行

    三个描述方法

    描述方式特点优缺点真题角度
    自然语言用日常语言描述步骤✅通俗易懂
    ❌易产生歧义
    判断题:自然语言描述算法最不容易产生歧义?❌
    流程图用图形符号表示流程✅清晰直观
    ❌绘制繁琐
    根据流程图写程序(代码填空)
    伪代码介于自然语言和编程语言之间✅结构清晰
    ✅无严格语法限制
    将伪代码转换为C++代码

    枚举算法

    定义:枚举算法是从可能的解集合中一一列举各元素,用题目给定的检验条件判定哪些是有效解

    本质:利用计算机”算得快”的特点,尝试所有可能性

    判断标准:第一时间分析数据规模

    数据规模可行性
    n104✅ 枚举完全可行
    n105⚠️ 需谨慎(O(n2)不可行)
    n106及以上❌ 危险,一般不适合直接枚举

    模拟算法

    定义:按照题目描述的规则,一步步用代码还原过程,直到得到最终结果

    核心思想:题目怎么说,代码就怎么写,忠实还原规则

    考察能力

    • 题意理解是否准确
    • 基本语法熟练度(变量、循环、条件判断)
    • 边界条件处理是否细心

    出题类型

    • 过程模拟:小杨储蓄
    • 规则模拟:数组清零
    • 递推模拟:分糖果
  • 位运算实战

    位运算符

    运算符名称运算规则记忆口诀
    &按位与两个二进制位都为1时结果为1,否则为0全1出1,有0出0
    |按位或两个二进制位都为0时结果为0,否则为1有1出1,全0出0
    ^按位异或两个二进制位相同时结果为0,不同时结果为1相同为0,不同为1
    ~按位取反单目运算符,0变1,1变0(包含符号位全部翻转
    <<左移高位丢弃,低位补02n2n
    >>右移低位丢弃,高位补符号位(算术右移)2n2n(向下取整)

    规则与运算

    按位与(&):清零与截取

    核心性质

    • x & 1:取x的最低位(判断奇偶)
    • x & (~0 << n):将低n位清零(右侧低位左侧高位)
    • 与0运算:x & 0 = 0

    按位或(|):置1操作

    核心性质

    • x | (1 << n):将第n位设为1
    • 或0不改变:x | 0 = x

    按位异或(^):不进位加法与交换

    核心性质(高频考点):

    • x ^ x = 0(自身异或得0)
    • x ^ 0 = x(与0异或不变)
    • x ^ y ^ x = y(自反性)
    • 异或满足交换律和结合律

    交换算法

        // 临时变量交换
        int temp = a;
        a = b;
        b = temp;
    
        // 异或交换(三步异或,只适用整数)
        a = a ^ b;  // 第一步:a = a ^ b
        b = a ^ b;  // 第二步:b = (a ^ b) ^ b = a
        a = a ^ b;  // 第三步:a = (a ^ b) ^ a = b
    
        // 加减法交换(可能有溢出风险)
        a = a + b;  // 第一步:a = a + b
        b = a - b;  // 第二步:b = (a+b) - b = a
        a = a - b;  // 第三步:a = (a+b) - a = b

    左移(<<)与右移(>>):乘除2的幂

    左移规则

    • x << n = x×2n
    • 高位丢弃,低位补0
    • 可能改变符号位(溢出)

    右移规则(重点!)

    • 正数右移:高位补0(逻辑右移)
    • 负数右移:高位补1(算术右移,保持符号位)
    • x >> n = x÷2n(向下取整)

    常用公式及用法

    恒等式

    操作结果应用场景
    x & xx
    x | xx
    x ^ x0判断相等、去重
    x & 00清零
    x | 0x
    x ^ 0x
    x & 1x的最低位判断奇偶
    x << nx×2n快速乘
    x >> nx÷2n快速除

    判断奇偶

    if (a & 1) {
        // a是奇数
    } else {
        // a是偶数
    }

    判断相等

    if ((a ^ b) == 0) {
        // a == b
    }

    运算符优先级

    从高到低:

    1. ~(取反)—— 同!++等单目运算符
    2. <<>>(移位)—— 比加减法低
    3. &(与)
    4. ^(异或)
    5. |(或)
    6. &=^=|=<<=>>=(复合赋值)

    常见陷阱

    陷阱错误写法正确写法
    优先级混淆x & 3 == 1(x & 3) == 1
    负数的右移误以为负数右移补0负数右移补1(算术右移)
    对有符号数左移溢出int << 31可能变成负数注意范围
    取反符号位以为~不影响符号位~所有位取反,包括符号位
  • 进制运算实战

    相关概念

    要素定义举例(十进制)
    数位数字符号在数中所处的位置数字5在百位、十位、个位
    基数该进制允许使用的数字符号个数十进制基数为10
    位权某一位上的1所表示的实际数值大小百位的权是10²=100

    基本特征

    进制基数数字符号进位规则
    二进制(B)20, 1逢二进一
    八进制(O/Q)80~7逢八进一
    十进制(D)100~9逢十进一
    十六进制(H)160~9, A(10), B(11), C(12), D(13), E(14), F(15)逢十六进一

    进制转换

    其他进制->十进制方法:将每位数字乘以该位的位权,再求和。

    • 整数部分(从右往左,第i位权为 基数ⁱ⁻¹)
    • 小数部分(从左往右,第j位权为 基数⁻ʲ)

    十进制->其他进制方法

    • 整数部分:除基取余,逆序排列
    • 小数部分:乘基取整,顺序排列

    二进制<->八进制/十六进制方法:分组转换

    转换类型方法规则
    二转八三位一组从小数点开始,整数部分向左每3位一组,小数部分向右每3位一组,不足补0
    八转二一位拆三位每位八进制数拆成3位二进制
    二转十六四位一组从小数点开始,整数部分向左每4位一组,小数部分向右每4位一组,不足补0
    十六转二一位拆四位每位十六进制数拆成4位二进制

    八进制/十六进制<->十进制方法:与二进制类似,基数从2换成8或16

    表示方法

    进制前缀示例十进制值
    十进制100100
    八进制007561
    十六进制0x 或 0X0x6096
    二进制(C++14起)0b 或 0B0b101010

    C++应用

    #include <iostream>
    #include <iomanip>
    using namespace std;
    int main() {
        cout << oct << 35 << endl;   // 输出八进制:43
        cout << dec << 35 << endl;   // 输出十进制:35
        cout << hex << 35 << endl;   // 输出十六进制:23
        cout << bitset<8>(35) << endl;  // 输出二进制:00100011
        return 0;
    }

    易错点

    1. 八进制数字范围是0~7,不会出现8或9——判断题高频陷阱
    2. 二进制转八进制/十六进制时,整数部分从右向左分组,小数部分从左向右分组,不足要补0
    3. C++中八进制以0开头,不是0o0O(Python才是)——客观题干扰项
    4. 十六进制A~F不区分大小写,但C++代码中通常用大写
    5. 负数的进制转换不在三级范围内,三级只考察正整数——注意题目限定条件
    6. 二进制小数转十进制时,2⁻¹=0.5,2⁻²=0.25,2⁻³=0.125——需熟练掌握小数位权
  • 原码、反码、补码实战

    计算机内部只能存储0和1,对于整数,需要一种方式来表示正数和负数。原码、反码、补码就是三种不同的编码方案。

    现代计算机统一使用补码来存储有符号整数。原因:

    • 补码可以将减法转化为加法运算
    • 补码没有“正负0”的问题
    • 补码的符号位可以直接参与运算

    原码

    规则:最高位表示符号(0正1负),其余位表示数值的绝对值。

    数值原码(8位)
    +10000 0001
    -11000 0001
    +00000 0000
    -01000 0000
    +1270111 1111
    -1271111 1111

    范围:-127 ~ +127(8位),有正负零两个编码。

    反码

    规则

    • 正数的反码 = 原码
    • 负数的反码 = 符号位不变,数值位按位取反
    数值原码反码
    +10000 00010000 0001
    -11000 00011111 1110
    +00000 00000000 0000
    -01000 00001111 1111
    +1270111 11110111 1111
    -1271111 11111000 0000

    范围:-127 ~ +127,仍有正负零问题。

    补码

    规则

    • 正数的补码 = 原码
    • 负数的补码 = 反码 + 1
    数值原码反码补码
    +10000 00010000 00010000 0001
    -11000 00011111 11101111 1111
    +00000 00000000 00000000 0000
    -01000 00001111 11110000 0000
    +1270111 11110111 11110111 1111
    -1271111 11111000 00001000 0001
    -1281000 0000

    范围:-128 ~ +127(8位),只有一个零的编码

    注意:-128没有对应的原码和反码,但补码表示为1000 0000。

    补码计算推导

    减法可以转化为加法

    公式:A – B = (A的补码) + (-B的补码)

    示例:-5-3

    -5的补码1111 1011
    -3的补码1111 1101
    加法1111 1011 + 1111 1101 = 1 1111 1000
    取低8位1111 1000
    转十进制补码1111 1000,减1得1111 0111,取反得1000 1000 = -8 ✅

    示例:-128-1(溢出)

    -128补码1000 0000
    -1补码1111 1111
    加法1000 0000 + 1111 1111 = 1 0111 1111
    取低8位0111 1111 = 127

    相关考点

    概念辨析:

    • 正数、负数的原码、反码、补码之间的换算关系
    • 补码存在的原因

    范围记忆:

    • 8位有符号整数的范围:-128~127
    • 8位原码/反码的范围:-127~127

    计算题相关:

    • 已知原码求补码
    • 已知补码求真值
    • 补码加减法结果判断
  • 考级集训课:GESP三级

    CCF编程能力等级认证考试大纲

    编号知识块知识点
    1数据编码原码、反码、补码
    2进制转换二进制、八进制、十进制、十六进制
    3位运算与(&)、或(|)、非(~)、异或(^)、左移(<<)、右移(>>)
    4算法与描述模拟法、枚举法
    自然语言描述、流程图描述、伪代码描述
    5数据结构一维数组
    6字符串及其函数大小写转换、字符串搜索、分割、替换等

    数据编码

    原码、反码、补码

    原码、反码、补码实战

    进制转换

    进制运算初步

    进制运算补充

    进制运算实战

    位运算

    位运算初步

    位运算实战

    算法与描述

    入门算法:枚举、模拟

    算法描述

    算法与描述实战

    数据结构

    一维数组

    字符串及其函数

    字符串基本操作

    字符串常用功能

    字符数组基本用法

  • 考级集训课:GESP五级

    CCF编程能力等级认证考试大纲

    编号知识块知识点
    1初等数论素数与合数、最大公约数、最小公倍数、同余与模运算、约数与倍数、质因数分解、奇偶性
    辗转相除法(欧几里得算法)
    唯一分解定理
    素数表的埃氏筛法与线性筛法
    2算法复杂度的估算含多项式的算法复杂度
    含指数、对数的算法复杂度
    3高精度算法数组模拟高精度的加法、减法、乘法、除法
    4链表单链表、双链表、循环链表的创建、插入、删除、遍历、查找
    5二分算法二分查找算法
    二分答案算法(二分枚举法)
    6递归算法递归算法相关概念
    递归算法时间复杂度、空间复杂度
    递归的优化策略
    7分治算法归并排序算法
    快速排序算法
    8贪心算法贪心算法相关概念
    最优子结构

    初等数论

    初等数论概念梳理

    算法复杂度的估算

    算法复杂度概念梳理

    高精度算法

    高精度算法入门

    高精度加法

    高精度减法

    高精度乘法(高精乘以低精)

    高精度乘法

    高精度除法(高精除以低精)

    链表

    二分算法

    递归算法

    分治算法

    贪心算法

  • HTML:补充标签

    文本语义标签

    标签含义视觉效果使用场景
    <mark>标记/高亮黄色背景搜索结果关键词、重点提示
    <del>删除的文本删除线原价被划掉
    <ins>插入的文本下划线新增内容、修改记录
    <small>小号文本字体变小版权声明、免责说明、旁注
    <code>代码片段等宽字体展示一行代码,如<div>
    <pre>预格式文本保留空格和换行,等宽字体展示大段代码、ASCII艺术
    <blockquote>长引用自动缩进引用大段他人文字
    <q>短引用自动加引号引用一句话
    <cite>作品标题斜体引用书名、电影名、文章名
    <abbr>缩写虚线下划线+鼠标悬停显示全称HTMLCSS
    <address>联系信息斜体,独占一行作者邮箱、公司地址
    <time>日期时间无特殊样式文章发布日期、会议时间

    举例:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>文本语义标签演示</title>
    </head>
    <body>
        <h1>📝 网站更新公告</h1>
    
        <!-- 时间与作者 -->
        <p>发布日期:<time datetime="2026-05-14">2026年5月14日</time>  by <address style="display: inline;">房老师@管理部</address></p>
    
        <!-- 高亮与删除/插入 -->
        <p>新版发布:<mark>添加MBTI选择提示!</mark> 原价 <del>¥199</del>,现价 <ins>¥0</ins> <small>(限时优惠)</small></p>
    
        <!-- 代码展示 -->
        <p>操作命令:在终端输入 <code>mbti</code></p>
        <p>一段完整的代码示例:</p>
        <pre><code>
    请输入命令:
    checkin每日打卡
    nickname修改昵称
    sex修改性别
    mbti修改MBTI
    logout退出登录
    ➜ ~ mbti
    选E或I:喜欢跟朋友一起玩、精力无限(E)|喜欢独处,安静使你恢复能量(I)
    选S或N:学习知识时,关注步骤和应用(S)|学习知识时,琢磨原理和意义(N)
    选T或F:朋友倾诉烦恼,分析解决方案(T)|朋友倾诉烦恼,表达理解关心(F)
    选J或P:对于作业,喜欢制定计划完成(J)|对于作业,喜欢灵活压时完成(P)
    最终结果如:INTJ,INTP,ENTJ,ENTP,INFJ,INFP,ENFJ,ENFP,ISTJ,ISFJ,ESTJ,ESFJ,ISTP,ISFP,ESTP,ESFP 
    请输入符合你性格特点的MBTI类型 (4个字符):
    ➜ ~ INTJ
    MBTI已更新为: INTJ
        </code></pre>
    
        <!-- 引用 -->
        <p>正如某位大佬所说:<q>代码是写给人看的</q>。</p>
        <blockquote cite="https://example.com/article">
            <p>HTML的语义化非常重要,不仅利于SEO,更能让屏幕阅读器正确理解内容。</p>
            <footer>—— <cite>《Web开发指南》</cite></footer>
        </blockquote>
    
        <!-- 缩写 -->
        <p>你正在学习 <abbr title="HyperText Markup Language">HTML</abbr> 和 <abbr title="Cascading Style Sheets">CSS</abbr>。</p>
    </body>
    </html>

    内容分区标签

    标签含义相当于使用场景
    <header>页眉包含logo、导航、标题的区域网页顶部、文章头部
    <nav>导航链接主要导航菜单顶部菜单、侧边栏菜单
    <main>主内容页面核心独有的内容每个页面只出现一次
    <article>独立文章可独立分发的内容博客文章、新闻条目、产品卡片
    <section>章节内容块,通常带标题文章的章节、网页的功能区块
    <aside>侧边栏与主内容间接相关侧边广告、相关链接、作者简介
    <footer>页脚底部信息版权声明、联系方式、备案号

    相当于有意义的div

    交互类标签

    标签含义作用示例
    <details>详情/折叠面板点击可展开隐藏内容常见于FAQ、折叠菜单
    <summary>总结/标题配合<details>,作为可见的标题点击<summary>展开/收起
    <dialog>对话框弹出提示框(需配合JS的.showModal()模态框、确认弹窗
    <progress>进度条显示任务完成百分比上传进度、下载进度
    <meter>度量条显示已知范围内的数值磁盘使用量、投票结果

    示例:

    <!-- 折叠面板 -->
    <details>
        <summary>📖 展开查看答案</summary>
        <p>HTML是超文本标记语言,用于构建网页结构。</p>
    </details>
    
    <!-- 进度条 -->
    <p>下载进度:<progress value="70" max="100">70%</progress></p>
    
    <!-- 度量条(默认绿色,可根据阈值变颜色) -->
    <p>电量:<meter value="0.8">80%</meter></p>
    <p>内存使用:<meter value="0.9" min="0" max="1" low="0.5" high="0.8" optimum="0.6">90%</meter></p>

    嵌入类标签

    标签含义作用典型用途
    <iframe>内联框架在当前页面嵌入另一个网页嵌入YouTube视频、Google地图、第三方小工具
    <embed>嵌入外部资源嵌入插件内容(Flash、PDF等)已过时,尽量用<object><iframe>代替
    <object>对象嵌入图片、PDF、Flash等嵌入PDF文档
    <iframe> 重点内联框架嵌入另一个HTML页面最常用:嵌入视频、地图

    列表标签(定义列表)

    标签含义作用
    <dl>定义列表包裹整个列表
    <dt>定义术语要解释的名词
    <dd>定义描述对名词的解释

    示例

    <h3>前端三剑客</h3>
    <dl>
        <dt>HTML</dt>
        <dd>超文本标记语言,负责网页结构。</dd>
        
        <dt>CSS</dt>
        <dd>层叠样式表,负责网页样式。</dd>
        
        <dt>JavaScript</dt>
        <dd>脚本语言,负责网页行为。</dd>
    </dl>

    转义字符

    显示结果实体名称实体编号说明
    <&lt;&#60;小于号
    >&gt;&#62;大于号
    &&amp;&#38;&符号
    "&quot;&#34;双引号
    '&apos;&#39;单引号
    &nbsp;&#160;不换行空格
    ©&copy;&#169;版权符号
    ®&reg;&#174;注册商标
    ¥&yen;&#165;人民币/日元符号

  • [GESP2512三级] ⼩杨的智慧购物

    模拟+桶思维

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n,m,k,p,a[100005]={};
        cin>>m>>n;
        for(int i=1;i<=n;i++){
            cin>>k>>p;
            if(a[k]==0) a[k]=p;
            else if(a[k]>p) a[k]=p;
        }
        int sum=0;
        for(int i=1;i<=m;i++){
            sum+=a[i];
        }
        cout<<sum;
        return 0;
    }
  • [GESP2512三级] 密码强度

    模拟+字符串操作

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        string s;
        cin>>n;
        while(n--){
            cin>>s;
            int a=0,b=0,c=0;
            if(s.size()>=8) a++;
            for(int i=0;i<s.size();i++){
                if(s[i]>='0'&&s[i]<='9'){
                    b++;
                }
                if(s[i]>='A'&&s[i]<='Z'){
                    c++;
                }
            }
            if(a&&b&&c) cout<<"Y\n";
            else cout<<"N\n";
        }
        return 0;
    }
  • [GESP2509三级] ⽇历制作

    数据推导+字符串操作

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int m;
        cin>>m;
        cout<<"MON TUE WED THU FRI SAT SUN\n";
        int i=1,n=31,k=0;
        if(m==2) n=28;
        else if(m==4||m==6||m==9||m==11) n=30;
        switch(m){
            case 1:case 10:k=2;break;
            case 2:case 3:case 11:k=5;break;
            case 4:case 7:k=1;break;
            case 5:k=3;break;
            case 6:k=6;break;
            case 8:k=4;break;
        }
        string s=string(k*4,' ');
        cout<<s;
        for(int i=1;i<=n;i++){
            printf("%3d ",i);
            if((i+k)%7==0) cout<<endl;
        }
        return 0;
    }
  • [GESP2509三级] 数组清零

    模拟

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n,a[105]={};
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        int cnt=0;
        while(true){
            int ak=0,k=-1,aj=101;
            for(int i=1;i<=n;i++){
                if(a[i]>=ak){
                    ak=a[i];
                    k=i;
                }
                if(a[i]!=0&&a[i]<aj){
                    aj=a[i];
                }
            }
            if(aj==101) break;
            a[k]=ak-aj;
            cnt++;
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2603三级] 凯撒密码

    字符串操作

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
    	string a,b,c;
    	cin>>a>>b>>c;
    	int k=b[0]-a[0];
    	if(k<0) k+=26;
    	for(int i=0;i<c.size();i++){
    		c[i]-=k;
    		if(c[i]<'A') c[i]+=26;
    		cout<<c[i]; 
    	} 
    	return 0;
    }
  • [GESP2603三级] ⼆进制回⽂串

    进制+回文
    #include <bits/stdc++.h>
    using namespace std;
    string tento2(int i){
        string ans;
        while(i!=0){
            int ys=i%2;
            ans+=to_string(ys); // 111000
            i/=2;
        }
        return ans;
    }
    bool ishuiwen(string s){
        string t=s; // 123
        reverse(t.begin(),t.end());  // 倒序
        return t==s;
    }
    int main(){
        int n;
        cin>>n;
        int ans=0;
        for(int i=1;i<=n;i++){
            string k=tento2(i);
            if(ishuiwen(k)){
                ans++;
            }
        }
        cout<<ans;
        return 0;
    }
  • [GESP2503三级] 词频统计

    字符串操作

    #include <iostream>
    #include <map>
    using namespace std;
    map<string,int> a;
    int n,b=0;
    string s,ans;
    int main(){
        cin>>n;
        while(n--)
        {
            cin>>s;
            for(int i=0;i<s.size();i++) 
                if(s[i]<95) 
                    s[i]+=32;
            a[s]++;
            if(a[s]>b)
            {
                b=a[s];
                ans=s;
            }
        }
        cout<<ans;
        return 0;
    }
  • [GESP2409三级] 平衡序列

    模拟

    #include <iostream>
    using namespace std;
    int main()
    {
        int t;
        cin >> t;
        while (t--)
        {
            int n;
            cin >> n;
            int a[10005] = {};
            int sum[10005] = {};
            for (int i = 1; i <= n; i++)
            {
                scanf("%d", a + i);
                sum[i] = sum[i - 1] + a[i];
            }
            bool f = false;
            for (int i = 1; i <= n; i++)
            {
                if (sum[i] * 2 < sum[n])
                    continue;
                if (sum[i] * 2 == sum[n])
                    f = true;
                break;
            }
            if (f)
                cout << "Yes\n";
            else
                cout << "No\n";
        }
        return 0;
    }
  • [GESP2406三级] 移位

    字符串操作

    #include <iostream>
    using namespace std;
    int main()
    {
        int n;
        cin >> n;
        string s = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ";
        cout << s.substr(n % 26, 26);
        return 0;
    }
  • [GESP2403三级] 字母求和

    字符串操作

    #include <iostream>
    using namespace std;
    int main()
    {
        int n;
        string s;
        cin >> n >> s;
        int ans = 0;
        for (int i = 0; s[i]; i++)
        {
            if (s[i] <= 90)
                ans -= s[i];
            else
                ans += s[i] - 96;
        }
        cout << ans;
        return 0;
    }
  • [GESP2312三级] 单位转换

    模拟

    #include <iostream>
    using namespace std;
    int main()
    {
        int n;
        cin >> n;
        while (n--)
        {
            int a;
            string b, c, d, e;
            int l = 1000;
            cin >> a >> b >> c >> d >> e;
            if (b == "km" && e == "mm" || b == "kg" && e == "mg")
                l = 1000000;
            cout << a << " " << b << " " << c << " " << a * l << " " << e << endl;
        }
        return 0;
    }
  • [GESP2312三级] 小猫分鱼

    贪心+逆向思维

    #include <iostream>
    using namespace std;
    int main()
    {
        int n, i;
        cin >> n >> i;
        int x = 0, y = 1;
        while (1)
        {
            x = y * n + i;
    		int j=0;
            for(;j<n-1;j++)
            {
                if (x % (n - 1))
                {
                    break;
                }
                x = x / (n - 1) * n + i;
            }
            if (j==n-1)
                break;
            y++;
        }
        cout << x;
        return 0;
    }
  • [GESP2309三级] 进制判断

    字符串操作+进制常识

    #include <iostream>
    using namespace std;
    int main()
    {
        int n;
        string s;
        cin >> n;
        while (n--)
        {
            cin >> s;
            int a = 1, b = 1, c = 1, d = 1;
            for (int i = 0; i < s.size(); i++)
            {
                if (s[i] > '1')
                    a = 0;
                if (s[i] > '7')
                    b = 0;
                if (s[i] > '9')
                    c = 0;
                if (s[i] > 'F')
                    d = 0;
            }
            cout << a << " " << b << " " << c << " " << d << '\n';
        }
        return 0;
    }
  • [GESP2306三级] 密码合规检测

    字符串操作

    #include <iostream>
    using namespace std;
    int main()
    {
        string s;
        cin >> s;
        s += ',';
        for (int i = 0, j = 0; i < s.size(); i++)
        {
            if (s[i] == ',')
            {
                string str = s.substr(j, i - j);
                j = i + 1;
                bool a = false, b = false, c = false, d = false, e = false;
                for (int p = 0; p < str.size(); p++)
                {
                    if (string("!@#$").find(str[p]) != string::npos)
                        b = true;
                    else if (str[p] >= '0' && str[p] <= '9')
                        c = true;
                    else if (str[p] >= 'a' && str[p] <= 'z')
                        d = true;
                    else if (str[p] >= 'A' && str[p] <= 'Z')
                        e = true;
                    else
                        a = true;
                }
                if (c + d + e < 2 || !b || a || str.size() < 6 || str.size() > 12)
                    continue;
                cout << str << endl;
            }
        }
        return 0;
    }
  • [GESP2409三级] 回文拼接

    字符串操作

    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
     int n;
     cin>>n;
     string a[100];
     int flag[100]={};
     for(int i=0;i<n;i++)
     {
      cin>>a[i];
      for(int j=2;j<a[i].size()-1;j++)
      {
       	string b=a[i].substr(0,j);
       	string b1=b;
       	string v=a[i].substr(j,a[i].size()-j);
       	string v1=v;
       	reverse(b.begin(),b.end());
       	reverse(v.begin(),v.end());
       	//cout<<b1<<" "<<v1<<endl;
       	if(b==b1&&v==v1)
       	{
       	flag[i]=1;	
       	break;
    	}
      }	
     }
     for(int i=0;i<n;i++)
     {
     	if(flag[i]==1)
     	{
     	 cout<<"Yes"<<endl;	
    	 }
    	 else
    	 {
    	 	cout<<"No"<<endl;
    	 }
     }
    }
    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        while(n--){
            string s;
            cin>>s;
            bool f=false;
            for(int i=2;i<s.size()-1;i++){
                string a1,b1,a2,b2;
                for(int j=0;j<i;j++) a1+=s[j];
                for(int j=i;j<s.size();j++) b1+=s[j];
                for(int j=i-1;j>=0;j--) a2+=s[j];
                for(int j=s.size()-1;j>=i;j--) b2+=s[j];
                if(b1==b2&&a1==a2){
                    f=true;
                    break;
                }
            }
            if(f) cout<<"Yes\n";
            else cout<<"No\n";
        }
        return 0;
    }
  • [GESP2412三级] 数字替换

    模拟

    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
    	int n;
    	cin>>n;
    	int k;
    	cin>>k;
    	int a[100005]={};
    	int max=-100005,min=100005;
    	for(int i=0;i<n;i++)
    	{
    		cin>>a[i];
    		if(max<a[i])
    		{
    		max=a[i];
    		}	
    		if(min>a[i])
    		{
    		min=a[i];
    		}	
    	}
    	for(int i=0;i<n;i++)
    	{
    		if(a[i]>k)
    		{
    		cout<<max<<" ";
    		}
    		if(a[i]<k)
    		{
    		cout<<min<<" ";
    		}
    		if(a[i]==k)
    		{
    		cout<<k<<" ";
    		}
    	}
    }
    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n,k,a[100005]={},ba=-100000,sa=100000;
        cin>>n>>k;
        for(int i=0;i<n;i++){
            cin>>a[i];
            if(a[i]>ba) ba=a[i];
            if(a[i]<sa) sa=a[i];
        }
        for(int i=0;i<n;i++){
            if(a[i]<k) cout<<sa<<" ";
            else if(a[i]>k) cout<<ba<<" ";
            else cout<<k<<" ";
        }
        return 0;
    }
  • [GESP2412三级] 打印数字

    星阵问题+二维数组

    #include <bits/stdc++.h>
    using namespace std;
    string str[5][4]={
        ".....","****.",".....",".....",
        ".***.","****.","****.","****.",
        ".***.","****.",".....",".....",
        ".***.","****.",".****","****.",
        ".....","****.",".....","....."
    };
    int main(){
        string s;
        cin>>s;
        for(int i=0;i<5;i++){
            for(int j=0;j<s.size();j++){
                cout<<str[i][s[j]-'0'];
            }
            cout<<endl;
        }
    }
    #include <bits/stdc++.h>
    using namespace std;
    int main()
    {
    	string n;
    	cin>>n;   
    	int sum = n.size(); 
    	for(int j=0;j<5;j++)      // 控制行
    	{	
    		string b = n;// "02230"
    		for(int i=0;i<sum;i++) // 控制列
    		{
    			if(b[i]=='0')
    			{
    				if(j==0||j==4)
    				{
    					cout<<".....";
    				}
    				else
    				{
    					cout<<".***.";
    				}	
    			}
    			else if(b[i]=='1')
    			{
    				cout<<"****.";
    			}
    			else if(b[i]=='2')
    			{
    				if(j==0||j==2||j==4)
    				{
    					cout<<".....";
    				}
    				else if(j==1) 
    				{
    					cout<<"****.";
    				}
    				else
    				{
    					cout<<".****";
    				}
    			}
    			else if(b[i]=='3') 
    			{
    				if(j==0||j==2||j==4)
    				{
    					cout<<".....";
    				}
    				else
    				{
    					cout<<"****.";	
    				}
    			}
    		}
    		cout<<endl;
    	}
    }
  • [GESP2506三级] 奇偶校验

    进制转换+统计

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n,c[105]={};
        cin>>n;
        for(int i=0;i<n;i++) cin>>c[i];
        int cnt=0;
        for(int i=0;i<n;i++){
            int j=c[i];
            while(j){
                if(j%2==1) cnt++;
                j/=2;
            }
        }
        cout<<cnt<<" ";
        cout<<(cnt%2==1?1:0);
        return 0;
    }
  • [GESP2506三级] 分糖果

    注意数据范围

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        long long n,a[1005],cnt=0;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
            if(a[i]<=a[i-1]) a[i]=a[i-1]+1;
            cnt+=a[i];
        }
        cout<<cnt;
        return 0;
    }
  • 二维数组

    二维数组本质上是一个数组的数组,可以理解为一个表格或矩阵,有行和列两个维度。

    二维数组的基本概念

     // 二维数组的逻辑结构
     int matrix[3][4];  // 3行4列的矩阵
     ​
     // 可视化表示:
     // 列:  0  1  2  3
     // 行0 [ ][ ][ ][ ]
     // 行1 [ ][ ][ ][ ]
     // 行2 [ ][ ][ ][ ]

    二维数组的声明和初始化

     #include <bits/stdc++.h>
     using namespace std;
     int main() {
         // 1. 声明时指定大小
         int arr1[2][3];  // 2行3列,未初始化,包含随机值
         int arr2[2][3]={};  // 初始化全为0
         
         // 2. 声明并初始化(嵌套花括号)
         int arr3[2][3] = {
             {1, 2, 3}, // 初始化arr2[0]数组
             {4, 5, 6}  // 初始化arr2[1]数组
         };
         
         // 3. 省略内层花括号(按行优先顺序填充)
         int arr4[2][3] = {1, 2, 3, 4, 5, 6};  // 等价于上面
         int arr5[2][3] = {1, 2, 5, 6};  // 等价于{125}{600}
         
         // 4. 部分初始化(未指定的元素自动为0)
         int arr6[2][3] = {
             {1, 2},      // 等价于 {1, 2, 0}
             {4}          // 等价于 {4, 0, 0}
         };
         
         // 5. 省略第一维大小(必须指定第二维)
         int arr7[][3] = {
             {1, 2, 3},
             {4, 5, 6},
             {7, 8, 9}
         };  // 自动推断为3行
         
         return 0;
     }

    二维数组的访问和遍历

     #include <bits/stdc++.h>
     using namespace std;
     int main() {
         int matrix[3][4] = {
             {1, 2, 3, 4},
             {5, 6, 7, 8},
             {9, 10, 11, 12}
         };
         
         // 1. 访问单个元素
         cout << matrix[1][2] << endl;  // 输出7
         
         // 2. 修改元素
         matrix[0][0] = 100;
         
         // 3. 使用嵌套循环遍历
         for (int i = 0; i < 3; i++) {          // 遍历行
             for (int j = 0; j < 4; j++) {      // 遍历列
                 cout << matrix[i][j] << " ";
             }
             cout << endl;
         }
         return 0;
     }

    常用输入输出模板

     #include <bits/stdc++.h>
     using namespace std;
     int main() {
         int n, m; // n代表行,m代表列,
         int a[105][105]={}; // 初始化二维数组
         cin>>n>>m;
         for(int i=1;i<=n;i++){
             for(int j=1;j<=m;j++){
                 cin>>a[i][j];
             }
         }
         // 遍历输出数组
         for(int i=1;i<=n;i++){
             for(int j=1;j<=m;j++){
                 cout<<a[i][j]<<" ";
             }
             cout<<endl;
         }
         return 0;
     }

    扩展内容

    1. 矩阵转置
    2. 矩阵相加
    3. 螺旋遍历
    4. 二维一维数组互转

    总结要点

    1. 存储方式:行优先连续存储
    2. 索引:从0开始,arr[行][列]
    3. 初始化:可使用嵌套花括号
    4. 传参:第二维大小必须指定
    5. 内存布局:理解地址计算有助于优化
  • 计数排序(Counting Sort)

    计数排序是一种非比较型的排序算法。它不通过元素之间的比较来确定顺序,而是利用数组的索引来确定元素的正确位置,通过统计每个值出现的次数来实现排序

    算法原理

    1. 找出范围:找到待排序数组中的最大值和最小值,确定数据范围
    2. 统计频率:创建一个计数数组,统计每个值出现的次数
    3. 计算位置:对计数数组进行前缀和计算,得到每个值在排序后的最终位置
    4. 构建输出:根据计数数组将元素放到正确的位置

    C++实现例题

    基础版本

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         int n,x;
         cin>>n;
         int cnt[10005]={}; // 统计数组的大小由数据范围而定
         for(int i=1;i<=n;i++){
             cin>>x;
             cnt[x]++; // 统计次数
         }
         // 遍历统计数组时与n无关
         for(int i=0;i<10005;i++){
             if(cnt[i]!=0) cout<<i<<" "; // 排序并去重
         }
         // 遍历统计数组时与n无关
         for(int i=0;i<10005;i++){
             int j=cnt[i];
             while(j--) cout<<i<<" "; // 排序不去重
         }
         return 0;
     }

    特点总结

    优点:

    • 时间复杂度低,当 k = O(n) 时可以达到线性时间
    • 稳定排序
    • 不需要比较元素

    缺点:

    • 需要额外的空间,空间复杂度 O(k)
    • 当数据范围 k 很大时(如 [1, 1000000] 只有几个数),效率低下
    • 只能排序整数,对于浮点数或字符串需要特殊处理

    适用场景:

    • 数据范围较小且集中(k 与 n 同量级或更小)
    • 需要线性时间复杂度的稳定排序
    • 数据为整数类型

    优化技巧

    1. 使用最小值偏移:避免浪费空间
    2. 选择合适的排序场景:当 k > n log n 时,考虑使用比较排序
    3. 与其他排序结合:计数排序可作为基数排序的基础
  • 冒泡排序(Bubble Sort)

    冒泡排序是最基础的排序算法之一,名字来源于排序过程中,较大的元素像“气泡”一样逐渐“浮”到数组的顶端(或末端)。

    一、核心思想

    重复遍历要排序的列表,依次比较相邻的两个元素,如果顺序错误(比如前一个比后一个大),就交换它们的位置。 每一轮遍历都会将当前未排序部分的最大(或最小)元素“冒泡”到正确的位置。

    • 升序排序:每一轮将最大的元素放到末尾
    • 降序排序:每一轮将最小的元素放到末尾

    二、代码实现

    • 基础版冒泡
     int arr[5] = {6, 3, 8, 2, 5}; // 假设5个数据
     for (int i = 0; i < 5 - 1; i++) {
         // 内层循环:每轮比较相邻元素,范围逐渐缩小
         for (int j = 0; j < 5 - 1 - i; j++) {
             if (arr[j] > arr[j + 1]) {
                 // 交换相邻元素
                 int temp = arr[j];
                 arr[j] = arr[j + 1];
                 arr[j + 1] = temp;
             }
         }
     }
     for (int i = 0; i < 5 - 1; i++) {
         cout << arr[i] << " ";
     }
    • 优化版冒泡
     int arr[5] = {6, 3, 8, 2, 5}; // 假设5个数据
     bool swapped; // 标记
     for (int i = 0; i < 5 - 1; i++) {
         swapped = false;  // 标记本轮是否发生交换
     ​
         for (int j = 0; j < 5 - 1 - i; j++) {
             if (arr[j] > arr[j + 1]) {
                 // 交换
                 int temp = arr[j];
                 arr[j] = arr[j + 1];
                 arr[j + 1] = temp;
                 swapped = true;  // 发生了交换
             }
         }
     ​
         // 如果本轮没有交换,说明数组已有序,提前退出
         if (!swapped) {
             break;
         }
     }
     for (int i = 0; i < 5 - 1; i++) {
         cout << arr[i] << " ";
     }

    冒泡排序属于稳定排序。

  • 排序算法

    概念:

    将一个无序列表 [5, 2, 9, 1],整理成 [1, 2, 5, 9](升序)或 [9, 5, 2, 1](降序)的方法称为排序算法。

    内容:

    排序算法包含多种算法:冒泡排序、选择排序、插入排序、计数排序、快速排序、归并排序、桶排序、基数排序、堆排序等。

    排序算法主要关注:时间复杂度、空间复杂度、稳定性、是否原地排序,初期我们只需要了解稳定性的含义即可。

    稳定性:

    相等元素在排序后相对顺序不变,称为稳定排序,具有稳定性。

  • 进制运算补充

    进制标记

    • 二进制:只包含0和1,通常用下标2或前缀0b(0B)表示
      • 如:11012、0b0011、0B101
    • 八进制:包含0~7,通常用下标8或前缀0、0o、0O表示
      • 如:2378、0123、0o105、0O77
    • 十进制:包含0~9,可以用下标10表示,默认无特殊前缀,极特殊情况0d开头
      • 如:25510、777、0d222
    • 十六进制:只包含0和1,通常用下标16或前缀0x(0X)、#表示,汇编语言中以后缀H结尾标记且开头非数字时补0
      • 如:A3F16、0xA3F、#A3F、0A3FH、3AFH

    十进制转二进制(带小数)

    • 将十进制数126.375转换成二进制数
    • 整数部分正常转换:126->1111110
    • 小数部分反复乘以基数(二进制基数为2),获取整数部分,直到归零
      • 0.375✖️2=0.75(整数部分0)
      • 0.75✖️2=1.5(整数部分1)
      • 0.5✖️2=1.0(整数部分1,小数归零)0.375->0.011
    • 126.375->1111110.011

    二进制转十进制(带小数)

    • 将二进制数1110.0101转换成十进制数
    • 整数部分正常转换:1110->14
    • 小数部分除以基数的k次方(k从1开始从左向右递增),求和
      • 0 1 0 1
      • 0➗21+1➗22+0➗23+1➗24=0+0.25+0+0.0625=0.3125
    • 110.0101->14.3125
  • 原码、反码、补码

    原码

    • 原码、反码、补码是计算机中表示有符号整数的三种编码方式,核心目的是解决负数的表示问题,并让加减法统一用加法器实现。
    • 正数的原码、反码、补码完全一致。
      • 6的二进制为110,通常以8位二进制表示原码:00000110,反码:00000110,补码:00000110
    • 负数的原码:二进制左侧首位表示符号,0代表正,1代表负,其他位代表真值。
      • -6的二进制为-110,通常以8位二进制表示原码:10000110

    原码缺陷

    • 1、对于0来说,有+0和-0之分,那么就有两种表达方式(00000000和10000000)
    • 2、减法不能直接用加法电路实现(需要单独处理符号)(计算繁琐)

    反码

    • 规则:
      • 正数的反码与原码一致
      • 负数的反码:将负数的原码符号位不变,其他位取反(1变0,0变1)
        • 如:-6的原码:10000110,-6的反码:11111001
        • 负数的反码转原码:符号位不变,其他位取反

    反码缺陷

    • 1、对于0来说,有+0和-0之分,那么就有两种表达方式(00000000和10000000)(尚未解决)
    • 2、运算时可能需要循环进位,不够完美

    补码

    • 规则:
      • 正数的补码与原码一致
      • 负数的补码:
        • 1、负数的反码+1
          • -6反码:11111001,+1后:11111010
        • 2、负数的原码两边的1不变,中间取反
          • -6原码:10000110,中间取反后11111010

    补码特点

    • 1、只有一个0:00000000(10000000表示-128)
    • 2、减法可统一为加法:A – B = A +(-B的补码)
      • 2 – 3 = 2 + -3 = 0010(2的补码) + 1101(-3的补码) =1111(补码)=1001(原码)= -1
    • 3、最高位仍表示符号(0正1负)
  • 入门算法:枚举、模拟

    枚举算法

    枚举算法(Enumeration / Brute Force)是指:列举出所有可能的候选解,逐一验证每个候选是否满足问题要求,最终找到正确答案。

    • 核心思想:用计算机”不怕重复”的特点,把所有的可能性都试一遍
    • 通俗理解:”我不知道密码,但我从0000试到9999″
    • 别名:暴力枚举、穷举法

    枚举是最笨的方法,也是最可靠的方法——当你想不出其他解法时,枚举就是解法。


    枚举算法的三个核心要素

    要素说明示例
    解空间所有可能解的集合0~9999的所有数字
    枚举方式如何遍历解空间for循环、递归、位运算
    验证条件判断一个候选是否为解是否满足方程式

    枚举算法的基本结构

     // 枚举算法通用模板
     for (枚举所有可能) {
         if (验证条件成立) {
             记录答案或输出;
         }
     }

    枚举算法的优缺点

    优点缺点
    思路简单,不容易出错时间复杂度高
    一定能找到正确答案数据量大时无法使用
    不用动脑筋想数学规律可能产生重复解

    模拟算法

    模拟算法(Simulation)是指:按照问题描述的操作步骤,用代码”翻译”整个过程,一步步执行,最终得到结果。

    • 核心思想:忠实还原规则,跟着过程走
    • 通俗理解:就像按菜谱做菜——菜谱说放盐就放盐,说翻炒就翻炒
    • 别名:过程仿真、情景模拟

    模拟不需要聪明的数学公式,只需要耐心地把规则翻译成代码。


    模拟算法的四个核心要素

    要素说明示例
    状态模拟过程中需要维护的数据当前位置、当前分数、剩余时间
    规则每一步如何改变状态移动规则、计分规则
    初始状态模拟开始时的状态起点(0,0)、分数0
    终止条件模拟何时结束达到指定步数、触发某个条件

    模拟算法的基本结构

     // 模拟算法通用模板
     初始化状态;
     ​
     while (未达到终止条件) {
         根据规则更新状态;
         步数/时间推进;
     }
     ​
     输出最终状态;

    模拟算法的优缺点

    优点缺点
    思路直接,按步骤写代码可能很长
    不需要复杂数学推导容易遗漏边界条件
    结果可靠(规则正确时)效率可能不高

    模拟 vs 枚举 对比总结

    维度枚举算法模拟算法
    核心问题“有哪些可能?”“过程是怎样的?”
    思维方式穷举所有候选复现问题步骤
    关键动作生成 + 验证状态更新 + 规则翻译
    代码结构循环 + if判断循环 + 状态变量
    复杂度来源解空间大小过程步数
    典型问法“找出所有满足…”“执行后结果是什么?”

    一句话总结

    枚举是”我试试所有可能”——找答案 模拟是”我跟着规则走一遍”——复现过程 数据范围小用枚举,步骤清晰用模拟,两者可以结合使用。

  • 位运算初步

    位运算是指在二进制位的层面上直接对整数进行操作的运算。

    • 数据在计算机中以二进制(0/1)存储
    • 位运算比算术运算(+、-、*、/)快得多
    • 常用于:权限控制、状态压缩、加密、图形处理、底层优化

    1. 位运算符概念

    C++ 中的 6 个基本位运算符:

    运算符名称英文说明
    &按位与AND两位都为1 → 结果为1
    |按位或OR至少一位为1 → 结果为1
    ^按位异或XOR两位不同 → 结果为1
    ~按位取反NOT0变1,1变0
    <<左移SHL所有位向左移动,右侧补0
    >>右移SHR所有位向右移动,左侧补符号位

    注意:位运算符只能用于 intlongshortchar整数类型


    2. 位与(&)

    规则表

    ABA & B
    000
    010
    100
    111

    常见用途

    • 判断奇偶:if (x & 1) → 奇数
    • 取特定位:x & (1 << k) → 判断第k位是否为1

    3. 位或(|)

    规则表

    ABA | B
    000
    011
    101
    111

    常见用途

    • 设置特定位为1:x | (1 << k)
    • 权限合并:READ | WRITE | EXECUTE

    4. 位异或(^)

    规则表

    ABA ^ B
    000
    011
    101
    110

    重要性质

    • a ^ a = 0(自己异或自己得0)
    • a ^ 0 = a(与0异或不变)
    • a ^ b ^ b = a(两次异或还原)

    常见用途

    • 翻转特定位:x ^ (1 << k)
    • 交换两数(不用临时变量)
    • 找出出现奇数次的数

    5. 位非(~)

    规则

    • 0 变 1
    • 1 变 0
    • 包括符号位也会取反

    重要公式

     ~x = -x - 1

    常见用途

    • 清除特定位:x & ~(1 << k)
    • 获取全1掩码:~0

    6. 左移(<<)

    规则

    • 所有位向左移动
    • 右侧补 0
    • 左侧超出的位被丢弃

    数学意义

     a << n = a × 2ⁿ

    常见用途

    • 快速乘以2的幂
    • 构造掩码:(1 << k) 表示第k位为1

    7. 右移(>>)

    规则

    • 所有位向右移动
    • 左侧补符号位(正数补0,负数补1)
    • 右侧超出的位被丢弃

    数学意义

     a >> n = ⌊a / 2ⁿ⌋  (向负无穷取整)

    常见用途

    • 快速除以2的幂
    • 取高位数据
    • 树状数组中的 lowbit 操作

    8. 所有运算符速查表

    运算示例结果说明
    5 & 310101 & 0011 = 0001
    5 | 370101 | 0011 = 0111
    异或5 ^ 360101 ^ 0011 = 0110
    取反~5-6~x = -x-1
    左移5 << 110×2
    右移20 >> 110÷2

    9. 优先级提醒(重要!)

    位运算符优先级低于判等运算符

     // 错误
     if (x & 1 == 0)   // 实际是 x & (1==0) → x & 0
     ​
     // 正确
     if ((x & 1) == 0)

    口诀:位运算一律加括号,安全第一不出错。

  • 进制运算初步

    基本概念

    • 基数:每一位允许使用的数字符号个数
      • 二进制(Binary):基数 2,数字 0和1
      • 八进制(Octal):基数 8,数字 0–7
      • 十六进制(Hexadecimal):基数 16,数字 0–9, A–F (A=10, B=11, C=12, D=13, E=14, F=15)

    1. 十进制 → R 进制(R = 2、8、16)

    方法除 R 取余,倒序排列

    步骤:

    1. 用十进制数不断除以 R
    2. 记录每次的余数(0 ~ R-1)
    3. 直到商为 0 为止
    4. 将余数 从下往上(最后得到的余数在最前面)写出

    十六进制时,余数 10~15 要写成 A~F(偶尔也可以a~f)

    示例 1:十进制 25 → 二进制(R=2)

     25 ÷ 2 = 12 余 1 ↑
     12 ÷ 2 = 6 余 0 |
     6 ÷ 2 = 3 余 0 |
     3 ÷ 2 = 1 余 1 |
     1 ÷ 2 = 0 余 1 |

    结果:25₁₀ = 11001₂(余数倒序结果)


    示例 2:十进制 234 → 八进制(R=8)

     234 ÷ 8 = 29 余 2 ↑
     29 ÷ 8 = 3 余 5 |
     3   ÷ 8 = 0 余 3 |

    结果:234₁₀ = 352₈


    示例 3:十进制 479 → 十六进制(R=16)

     479 ÷ 16 = 29 余 15 (F) ↑
     29 ÷ 16 = 1 余 13 (D) |
     1   ÷ 16 = 0 余 1     |

    结果:479₁₀ = 1DF₁₆


    2. R 进制 → 十进制(R = 2、8、16)

    方法按权展开求和

    公式:(数)R=(每一位数字×R位数位置)\text{(数)}_R = \sum (\text{每一位数字} \times R^{\text{位数位置}})

    • 位数位置:从 右向左 从 0 开始编号

    十六进制中的 A~F 要先转成 10~15


    示例 1:二进制 11001₂ → 十进制

    1×24+1×23+0×22+0×21+1×20=16+8+0+0+1=251×2^4 + 1×2^3 + 0×2^2 + 0×2^1 + 1×2^0 = 16 + 8 + 0 + 0 + 1 = 25

    结果:11001₂ = 25₁₀


    示例 2:八进制 352₈ → 十进制

    3×82+5×81+2×80=3×64+5×8+2×1=192+40+2=2343×8^2 + 5×8^1 + 2×8^0 = 3×64 + 5×8 + 2×1 = 192 + 40 + 2 = 234

    结果:352₈ = 234₁₀


    示例 3:十六进制 1DF₁₆ → 十进制

    (D=13, F=15)

    1×162+13×161+15×160=1×256+13×16+15×1=256+208+15=4791×16^2 + 13×16^1 + 15×16^0 = 1×256 + 13×16 + 15×1 = 256 + 208 + 15 = 479

    结果:1DF₁₆ = 479₁₀


    3. N 进制 → M 进制(2、8、16 之间转换)

    统一方法先转十进制,再转目标进制

    为什么?因为十进制是我们最熟悉的“中间桥梁”

    通用公式:

    N进制按权展开十进制除M取余M进制N \text{进制} \xrightarrow{\text{按权展开}} \text{十进制} \xrightarrow{\text{除M取余}} M \text{进制}


    3.1 二进制 ↔ 八进制(特殊快捷方法)

    二进制 → 八进制三位一组(从右向左),每组转成一个八进制数字

    • 不够三位时左边补 0

    示例:1100101₂ → 八进制

     分组: 001 100 101
            1   4   5

    结果:145₈


    八进制 → 二进制每位八进制 → 3 位二进制

    示例:352₈ → 二进制

     3 → 011
     5 → 101
     2 → 010

    结果:011101010₂(可省略前导0 → 11101010₂)


    3.2 二进制 ↔ 十六进制(特殊快捷方法)

    二进制 → 十六进制四位一组(从右向左),每组转成一个十六进制数字

    示例:110111101011₂ → 十六进制

     分组: 1101 1110 1011
            D   E   B

    结果:DEB₁₆


    十六进制 → 二进制每位十六进制 → 4 位二进制

    示例:1DF₁₆ → 二进制

     1 → 0001
     D → 1101
     F → 1111

    结果:000111011111₂(→ 111011111₂)

  • [GESP2603二级] 画画

    经典星阵问题

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==1&&j==1||i==1&&j==n||i==n&&j==1||i==n&&j==n) cout<<"+";
                else if(i==1||i==n) cout<<"-";
                else if(j==1||j==n) cout<<"|";
                else cout<<"*";
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2512二级] 黄⾦格

    根据条件枚举所有可能性

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int h,w,x;
        cin>>h>>w>>x;
        int cnt=0;
        for(int r=1;r<=h;r++){
            for(int c=1;c<=w;c++){
                if(sqrt(pow(r,2)+pow(c,2))<=x+r-c){
                    cnt++;
                }
            }
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2509二级] 菱形

    0行(n/2

    1行中间(n/2+1,n/2-1

    2行(n/2+2,n/2-2

    以此类推,找规律

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                if(i<=n/2&&(j==n/2+i||j==n/2-i)) cout<<"#";
                else if(i>n/2&&(j==i-n/2||j==n/2+n-i-1)) cout<<"#";
                else cout<<".";
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2506二级] 幂和数

    using namespace std;
    int main(){
        int l,r;
        cin>>l>>r;
        int a[20]={1}; // 预处理:提前求出本题范围内2的幂
        for(int i=1;i<=19;i++) a[i]=a[i-1]*2;
        int cnt=0;
        for(int i=l;i<=r;i++){
            int n=i;
            bool f=false; // 假设不是幂合数
            for(int x=0;a[x]<=n;x++){
                if(f) break; // 避免重复计算
                for(int y=0;a[y]<=n;y++){
                    if(a[x]+a[y]==n){
                        cnt++;
                        f=true;
                        break;
                    }
                }
            }
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2503二级] 时间跨越

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int y,m,d,h,k;
        cin>>y>>m>>d>>h>>k;
        h+=k;
        if(h>23){
            d++;
            h-=24;
        }
        if(m==2){
            if(y%400==0||y%100!=0&&y%4==0){
                if(d==30){
                    m++;
                    d=1;
                }
            }
            else if(d==29){
                m++;
                d=1;
            }
        }
        else if(m==4||m==6||m==9||m==11){
            if(d==31){
                m++;
                d=1;
            }
        }
        else{
            if(d==32){
                m++;
                d=1;
            }
        }
        if(m==13){
            y++;
            m=1;
        }
        cout<<y<<" "<<m<<" "<<d<<" "<<h;
        return 0;
    }
  • [GESP2412二级] 数位和

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        long long n,a;
        cin>>n;
        long long ans=0;
        for(int i=1;i<=n;i++){
            cin>>a;
            long long sum=0;
            while(a){
                sum+=a%10;
                a/=10;
            }
            ans=max(sum,ans);
        }
        cout<<ans;
        return 0;
    }
  • [GESP2409二级] 小杨的N字矩阵

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(j==1||j==n||i==j) cout<<"+";
                else cout<<"-";
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2406二级] 计数

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n,k;
        cin>>n>>k;
        int c=0;
        for(int i=1;i<=n;i++){
            int t=i;
            while(t){
                if(t%10==k) c++;
                t/=10;
            }
        }
        cout<<c;
        return 0;
    }
  • [GESP2403二级] 小杨的日字矩阵

    星阵问题固定解法:

    1、双重循环,确定矩形宽高

    2、根据题目要求,确定特殊形状,本题日字矩阵,两边竖线(j==1或j==n),中间横线(i==1||i==n||i==(n+1)/2),其余皆是x

    不要写错每一行结束后的换行打印代码位置

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(j==1||j==n) cout<<"|";
                else if(i==1||i==n||i==(n+1)/2) cout<<"-";
                else cout<<"x";
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2312二级] 小杨的 H 字矩阵

    星阵问题固定解法:

    1、双重循环,确定矩形宽高

    2、根据题目要求,确定特殊形状,本题日字矩阵,两边竖线(j==1或j==n),中间横线(i==n+1/2`),其余皆是a

    不要写错每一行结束后的换行打印代码位置

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(j==1||j==n) cout<<"|";
                else if(i==(n+1)/2) cout<<"-";
                else cout<<"a";
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2309二级] 数字黑洞

    给定一个三位数,要求各个位不能相同(这句话是题目的自我限制,不需要你写条件再判断一次)

    1、输入一个数字

    2、判断是不是495,如果是,输出变换次数

    3、如果不是495,找出这个三位数的最大最小数字,重新组合成两个数进行计算,并统计变换次数

    4、重复2、3步骤,直到输出次数

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        int cnt=0;
        while(n!=495){
            int a=n%10,b=n/10%10,c=n/100;
            if(a<b) swap(a,b);
            if(a<c) swap(a,c);
            if(b<c) swap(b,c);
            int big=a*100+b*10+c;
            int small=c*100+b*10+a;
            n=big-small;
            cnt++;
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2306二级] 自幂数判断

    本题难点在于输入的数字长度不确定

    根据自幂数的特点,我们需要先统计输入的数字是几位数,才能确定几次方的和,最后再判断

    循环多组数据需要判断,每次判断前,需要把使用过的数据清零,避免原来的数据干扰新的判断

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int m,n;
        cin>>m;
        for(int i=1;i<=m;i++){
            cin>>n; // 接下来要判断n是否为自幂数
            int k=n,c=0; // k是替身,c是位数
            while(k>0){
                c++; // 统计位数
                k/=10;
            }
            int sum=0; // 求和
            k=n; // 再次恢复k的数据
            while(k>0){
                sum+=round(pow(k%10,c));
                k/=10;
            }
            if(n==sum) cout<<"T\n";
            else cout<<"F\n";
        }
        return 0;
    }
  • [GESP2303二级] 百鸡问题

    经典数学问题

    枚举出所有可能性,统计符合条件的方案,最后输出方案数

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int x,y,z,n,m;
        cin>>x>>y>>z>>n>>m;
        int cnt=0;
        for(int i=0;i<=m;i++){
            for(int j=0;j<=m;j++){
                int k=m-i-j;
                if(k<0) continue; 
                if(k%z!=0) continue;
                if(k/z+y*j+x*i==n) cnt++;
            }
        }
        cout<<cnt;
        return 0;
    }
  • 一维数组

    1. 数组的本质

    • 数组是一组相同类型数据的集合
    • 在内存中连续存储,每个元素占用相同大小的内存空间
    • 数组名代表数组首元素的地址(常量指针)

    2. 数组的特性

     特性1:固定大小
     - 定义后大小不可改变
     - 大小必须是编译时常量(C++标准)
     ​
     特性2:类型一致
     - 所有元素必须是同一数据类型
     ​
     特性3:连续存储
     - 元素在内存中地址连续
     - 可通过首地址+偏移量快速访问
     ​
     特性4:随机访问
     - 通过下标直接访问任意元素,时间复杂度O(1)

    3. 存储结构

     int arr[5] = {10, 20, 30, 40, 50};
     ​
     内存布局:
     地址:    1000   1004   1008   1012   1016
             +------+------+------+------+------+
     arr:    |  10  |  20  |  30  |  40  |  50  |
             +------+------+------+------+------+
     索引:     0      1      2      3      4

    4. 下标访问的本质(初学者了解)

     arr[i] 等价于 *(arr + i)
     - arr是首地址
     - i是偏移量
     - []是运算符,执行指针算术和解引用

    5. 边界概念

     定义:int arr[5];
     合法下标范围:0 ~ 4(共5个元素)
     ​
     重要概念:
     - 编译器不检查数组边界
     - 访问越界是未定义行为
     - 越界可能:覆盖其他变量、程序崩溃、隐藏bug

    6. 初始化的规则

     // 完全初始化:明确所有元素的值
     int a[3] = {1, 2, 3};
     ​
     // 部分初始化:未指定的元素自动初始化为0
     int b[5] = {1, 2};     // {1,2,0,0,0}
     ​
     // 默认初始化:不初始化,值为随机(垃圾值)
     int c[5];
     ​
     // 零初始化:所有元素为0
     int d[5] = {0};
     ​
     // 自动推断大小
     int e[] = {1,2,3};     // 大小推断为3

    7. 初始化vs赋值

     初始化:在创建时赋予初值
     赋值:创建后修改值
     数组定义后不能整体赋值:
     int a[3] = {1,2,3};
     int b[3];
     b = a;    // ❌ 错误!数组不能整体赋值

    8. 安全使用原则

     1. 永远确保下标不越界
     2. 永远初始化数组(避免随机值)

    9. 常用数组代码模版

     int n,a[100005]={};
     cin>>n;
     for(int i=1;i<=n;i++){
         cin>>a[i];
     }
     for(int i=1;i<=n;i++){
         cout<<a[i]<<" ";
     }
    
    
  • [GESP2603二级] 数数

    枚举所有数字并筛选统计

    数字拆分+累计

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int l,r;
        cin>>l>>r;
        int cnt=0;
        for(int i=l;i<=r;i++){
            int j=i;
            int t=0;
            while(j){
                t+=j%10==2;
                j/=10;
            }
            if(t==3) cnt++;
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2512二级] 环保能量球

    本题重点在于,行走的公里数包含了多少个奖励触发瞬间

    因此,环保能量值=行走的公里数+行走公里数/奖励间隔

    // 4155
    #include <iostream>
    using namespace std;
    int main(){
        int t,n,x;
        cin>>t;
        for(int i=1;i<=t;i++){
            cin>>n>>x;
            cout<<n+n/x<<endl;
        }
        return 0;
    }
  • [GESP2509二级] 优美的数字

    本题因为数位长度不同,数据范围较小,数据点及其明显,因此可以通过规律直接筛选:

    1、10以内的数字全符合条件

    2、10~99之间的数字全都是11的倍数

    3、100~999之间的数字全都是111的倍数

    4、1000以上只有1111符合条件

    #include <iostream>
    using namespace std;
    int main(){
        // 1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99,111,222,333,444,555,666,777,888,999,1111
        int n;
        cin>>n;
        int cnt=0;
        for(int i=1;i<=n;i++){
            if(i<10) cnt++;
            else if(i%11==0&&i<100) cnt++;
            else if(i%111==0&&i<1000) cnt++;
            else if(i==1111) cnt++;
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2506二级] 数三角形

    穷举所有a和b的可能性,判断a*b%2==0是否成立即可

    避免1,3与3,1的重复情况,可以使得:

    a的取值范围:1~n

    b的取值范围:a~n(保持b不小于a,就不会出现重复面积三角形)

    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        int cnt=0;
        for(int a=1;a<=n;a++){
            for(int b=a;b<=n;b++){
                if(a*b%2==0) cnt++;
            }
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2503二级] 等差矩阵

    绘制图形题固定解决思路:

    1、确定大小:按要求输出矩形

    2、造型:本题造型固定

    3、修改字符:通过已知条件,可以发现输出的内容恰好是i*j的计算结果

    #include <iostream>
    using namespace std;
    int main(){
        int n,m;
        cin>>n>>m;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                cout<<i*j<<" ";
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2412二级] 寻找数字

    凡是输入数据输出数据次数达到10万次,则需要scanf和printf优化输入输出的速度,否则超时

    利用循环完成多次输入的过程

    对于每一个数字,使用pow(a,0.25)完成对a的开四次方根,得到b,再判定b是否为正整数即可

    // 4091
    #include <bits/stdc++.h>
    using namespace std;
    int main(){
    	int t,a;
    	scanf("%d",&t);//cin>>t; // scanf
    	for(int i=1;i<=t;i++){
    		scanf("%d",&a);//cin>>a;
    		bool f=false;
    		int b;
    		for(b=1;b*b*b*b<=a;b++){
    			if(b*b*b*b==a){
    				f=true;
    				break;
    			}
    		}
    		if(f==true) printf("%d\n",b);//cout<<b<<endl; // printf
    		else printf("%d\n",-1);//cout<<-1<<endl; // printf
    	}
    	return 0;
    }
  • [GESP2409二级] 数位之和

    凡是输入数据输出数据次数达到10万次,则需要scanf和printf优化输入输出的速度,否则超时

    利用循环完成多次输入的过程

    对于每一个数字,使用求和以及数字拆分的技巧,完成数位和的判定

    // 4075
    #include <bits/stdc++.h>
    using namespace std;
    int main(){
    	int n,a;
    	scanf("%d",&n);//cin>>n;
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a);//cin>>a;
    		// 数字拆分+累加求和
    		int sum=0;
    		while(a>0){
    			sum+=a%10;
    			a/=10;
    		} 
    		if(sum%7==0){
    			printf("Yes\n"); // cout<<"Yes"<<endl;
    		}
    		else{
    			printf("No\n"); // cout<<"No"<<endl;
    		}
    	} 
    	return 0;
    }
  • [GESP2406二级] 平方之和

    从题目可知,因为x和y的平方和等于a,所以x和y的值一定不超过sqrt(a)

    方案1:对于每一个a,利用双层循环遍历所有的x和y的可能性,确定是否存在满足条件

    方案2:对于每一个a,利用单层循环遍历所有可能的x,根据公式计算出y,如果y是正整数,说明存在满足条件的情况

    #include <iostream>
    #include <cmath>
    using namespace std;
    int main(){
        int n,a;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a;
            bool f=false;
            for(int x=1;x*x<=a;x++){
                int y=sqrt(a-x*x);
                if(y*y+x*x==a){
                    f=true;
                    break;
                }
            }
            cout<<(f?"Yes\n":"No\n");
        }
        return 0;
    }
  • [GESP2403二级] 乘法问题

    累乘题

    计算过程中,判断是否超范围

    如果超范围,终止计算,最后根据计算结果输出答案

    #include <iostream>
    using namespace std;
    int main(){
        int n,a;
        cin>>n;
        int sum=1;
        for(int i=1;i<=n;i++){
            cin>>a;
            sum*=a;
            if(sum>1000000) break;
        }
        if(sum>1000000) cout<<">1000000";
        else cout<<sum;
        return 0;
    }
  • [GESP2312二级] 小杨做题

    模拟题

    类似斐波那契数列或辗转相除法的处理方式

    a+b=c,a=b,b=c

    输入abmn
    最开始做sum=a+b道题
    for(i:3~n):
    	每天做题数:a+b
        累计做题数量
        if:超出限制,停止做题
        else:a=b,b=c
    #include <iostream>
    using namespace std;
    int main(){
        int a,b,m,n,c;
        cin>>a>>b>>m>>n;
        int cnt=a+b;
        for(int i=3;i<=n;i++){
            c=a+b;
            cnt+=c;
            if(c>=m) break;
            a=b;
            b=c;
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2309二级] 小杨的X字矩阵

    绘制图形题固定解决思路:

    1、确定大小:按要求输出矩形

    2、造型:根据题目要求,分出不同形状的条件

    3、修改字符

    // 1、确定大小:按要求输出矩形
    输入n
    for(i:1~n): // 确定行数
    	for(j:1~n): // 确定每行字符数
    // 2、造型:根据题目要求,分出不同形状的条件
    对角线:i==j 或 i+j==n+1
    // 3、修改字符
    对角线打印 '+' 非对角线打印 '-'
    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==j || i+j==n+1) cout<<'+';
                else cout<<'-';
            }
            cout<<endl;
        }
        return 0;
    }
  • [GESP2306二级] 找素数

    1、确定范围

    2、判断每个数字是否是质数

    3、统计质数的个数

    输入a,b
    for(i:a~b):
    	如果i是质数:计数+1
    判断质数:穷举法(试除法),除了1和它本身,没有其他因数的数是质数
    #include <iostream>
    using namespace std;
    int main(){
        int a,b,cnt=0;
        cin>>a>>b;
        for(int i=a;i<=b;i++){
            bool f=true;
            for(int j=2;j<i;j++){
                if(i%j==0){
                    f=false;
                    break;
                }
            }
            if(f) cnt++;
        }
        cout<<cnt;
        return 0;
    }
  • [GESP2303二级] 画三角形

    绘制图形题固定解决思路:

    1、确定大小:按要求输出矩形

    2、造型:根据题目要求,将矩形多余部分去掉,保留形状部分

    3、修改字符

    // 1、确定大小:按要求输出矩形
    输入n
    for(i:1~n): // 确定行数
    	for(j:1~n): // 确定每行字符数
    // 2、造型:根据题目要求,将矩形多余部分去掉,保留形状部分
    第i行,输出i个字符
    for(i:1~n): // 行数
    	for(j:1~i): // 每行循环输出i个字符
    // 3、修改字符
    设定字符从'A'开始
    每输出一个字符,变成下一个字符
    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        char c='A';
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                cout<<c;
                c++;
                if(c>'Z') c='A';
            }
            cout<<endl;
        }
        return 0;
    }
  • 最大公约数

    定义

    最大公约数(Greatest Common Divisor, GCD)是指两个或多个整数共有约数中最大的一个。 例如,12 和 18 的公约数有 1、2、3、6,其中最大的 6 就是它们的最大公约数。 最大公约数在数学、密码学、分数化简等领域有广泛应用。

    方案

    • 枚举法(穷举法):从较小的数开始向下(或向上)逐一检验,找到最大的能同时整除两个数的数。
    • 辗转相除法(欧几里得算法):反复用较大数除以较小数取余,直到余数为零,此时除数即为最大公约数。
    • 更相减损法:古代《九章算术》中的方法,通过反复相减(或结合提取公因子 2)得到最大公约数。
    • Stein 算法:一种优化的大整数求 GCD 算法,主要针对二进制运算。

    具体代码

    枚举法(穷举法)

    输入两个正整数 (a, b),设较小数为 (minVal)。从 (minVal) 开始向下递减到 1,检查每个数是否能同时整除 (a) 和 (b)。第一个满足条件的数即为最大公约数。

    #include <iostream>
    using namespace std;
    
    int main() {
        int a, b;
        cin >> a >> b;
    
        // 取较小数
        int minVal = (a < b) ? a : b;
        int gcd = 1;
    
        // 从 minVal 向下枚举
        for (int i = minVal; i >= 1; i--) {
            if (a % i == 0 && b % i == 0) {
                gcd = i;
                break;  // 找到即退出循环
            }
        }
    
        cout << gcd << endl;
        return 0;
    }

    辗转相除法(欧几里得算法)

    用较大数除以较小数得到余数,然后用除数代替被除数,余数代替除数,重复此过程直到余数为 0,此时的除数就是最大公约数。 (算法中不需要判断大小,因为取余运算会自动处理)

    #include <iostream>
    using namespace std;
    
    int main() {
        int a, b;
        cin >> a >> b;
    
        int x = a, y = b;  // 保存原始值,以便输出时使用
        // 辗转相除
        while (y != 0) {
            int r = x % y;
            x = y;
            y = r;
        }
        // 此时 x 即为最大公约数
    
        cout << x << endl;
        return 0;
    }

    更相减损法(《九章算术》)

    1. 输入两个正整数 a 和 b。
    2. 若 a=b,则最大公约数为 a(或 b),算法结束。
    3. 若 a≠b,则用较大数减去较小数,用差替换较大数。
    4. 重复步骤 2 和 3,直到两数相等为止,此时相等的数即为最大公约数。
    #include <iostream>
    using namespace std;
    
    int main() {
        int a, b;
        cin >> a >> b;
    
        int x = a, y = b;
        // 当两数不相等时,不断用大数减小数
        while (x != y) {
            if (x > y)
                x = x - y;
            else
                y = y - x;
        }
        // 此时 x == y,即为最大公约数
    
        cout << x << endl;
        return 0;
    }

    总结

    方法优点缺点
    枚举法简单直观效率低,适合小整数
    辗转相除法效率高,稳定需要取模运算
    更相减损法古代算法,适合手工计算基础版效率较低,优化后较好

    以上三种方法均能正确计算最大公约数,实际编程中推荐使用辗转相除法,其代码简洁且效率最高。

  • 算法描述

    定义

    算法描述是指用某种方式将算法的逻辑步骤、控制流程和数据操作表达出来,以便于理解、分析、实现或交流。常见的算法描述方法有自然语言描述流程图描述伪代码描述。这三种方式从不同角度呈现算法,各有优劣,在实际应用中常结合使用。

    自然语言描述

    定义

    使用人类日常语言(如中文、英文)按顺序叙述算法的执行过程,不涉及编程语法,重在表达逻辑。

    特点

    • 通俗易懂,无需专业背景。
    • 描述可能不够精确,容易产生歧义。
    • 适合算法初步介绍或与人沟通。

    样例(辗转相除法求最大公约数)

    1. 输入两个正整数 a 和 b。
    2. 若 b 等于 0,则最大公约数为 a,算法结束。
    3. 否则,计算 a 除以 b 的余数 r。
    4. 将 a 赋值为 b,b 赋值为 r。
    5. 返回第 2 步继续执行。

    流程图描述

    定义

    使用图形符号(起止框、判断框、处理框、流程线等)按照算法执行顺序绘制流程图,直观展示算法的控制流。

    特点

    • 图形化,结构清晰,一目了然。
    • 便于发现算法中的分支和循环结构。
    • 绘制较繁琐,不适合过于复杂的算法。

    样例(辗转相除法求最大公约数)

    伪代码描述

    定义

    介于自然语言和编程语言之间的描述方式,使用结构化的控制语句(如 ifwhilefor)和数学符号,省略具体语法细节,突出算法逻辑。

    特点

    • 接近代码,易于转换为实际程序。
    • 比自然语言更精确,比流程图更简洁。
    • 无固定语法,可根据需要灵活表达。

    样例(辗转相除法求最大公约数)

    输入 a, b
    while b ≠ 0:
        r = a mod b
        a = b
        b = r
    end while
    输出 a

    对比


    对比项
    自然语言描述流程图描述伪代码描述
    表达方式自然语言(中文/英文)图形符号 + 文字结构化语句 + 数学符号
    精确度较低,易歧义较高,图形确定很高,接近程序逻辑
    可读性最易读,无需基础直观,适合展示结构需要一定编程思维
    转换代码需人工翻译需人工翻译或工具生成几乎可直接转换
    适用场景初步说明、教学设计阶段、文档展示算法设计、编程前导

    三种描述方式相辅相成:自然语言帮助理解意图,流程图看清结构,伪代码直接指导编程。

  • GESP一级客观题总结

    GESP C++一级考试的选择题(各15题)和判断题(各10题),按七大核心考点模块分类。

    一、计算机基础常识

    核心考点

    1. 计算机硬件功能划分(输入/输出设备、处理器、存储器)
    2. 行业科技热点概念(大模型、人形机器人传感器)
    3. 操作系统、编译器、IDE的基础功能与调试规则

    注意事项

    1. 传感器是输入设备(收集外部数据反馈),处理器是执行判断、运算的核心
    2. 大模型核心指大语言模型,属于AI常识积累
    3. IDE调试中可修改源程序,无需关闭文件,重新编译即可继续

    二、C++变量命名规则

    核心考点

    1. 变量名合法组成(字母、数字、下划线,无特殊字符)
    2. 变量名大小写敏感,关键字不可作为变量名
    3. 下划线可做首字符,汉语拼音可做变量名

    注意事项

    1. 非法字符:空格、减号(-)、井号(#)、点号(.)均不可出现在变量名中
    2. 关键字判断forfalse是关键字,printfkeywordscanfcincout非关键字,不可误判
    3. 变量名区分大小写(如firstFirst是两个变量,混用编译报错)

    三、运算符与表达式计算

    核心考点

    1. 算术运算符(+、-、*、/、%)优先级与计算规则
    2. 自增运算符(++):前置/后置区别、语法限制
    3. 赋值/复合赋值运算符:结合性、算术法交换变量
    4. 逗号表达式、逻辑非(!)、三目运算符的基础用法
    5. 不同类型数据混合运算的隐式类型转换

    注意事项

    1. 算术优先级:*、/、% > +、-,同级从左到右执行;取模%操作数必须为整数(浮点数参与报错)
    2. 自增规则:前置++先自增后使用,后置++先使用后自增;后置++不能作用于表达式
    3. 赋值结合性:从右到左,连续赋值/算术交换需注意变量临时值
    4. 逗号表达式:结果为最后一个表达式的值;逻辑非!:非0值取0,0值取1
    5. 隐式类型转换:int/float/double混合运算自动转高精度,无编译报错

    四、输入输出语法

    核心考点

    1. scanf输入分隔符规则(空格、制表符、回车均可)
    2. printf格式控制符:普通字符串、%02d、%%、%g等用法
    3. printf多余输出参数的处理规则

    注意事项

    1. scanf读取多个整型时,制表符/空格/回车均为有效分隔符,无需额外处理
    2. printf普通字符串直接输出(如{a+b}不会解析变量),格式符按规则格式化
    3. %%表示输出百分号%%02d补前导0至2位;多余输出参数直接忽略,无报错
    4. %g自动保留有效数字,输出缩短并非计算机故障

    五、流程控制语句(for/while循环)

    核心考点

    1. for/while循环的执行逻辑,循环变量的最终值
    2. 循环体范围:无大括号{}时仅紧跟一行代码
    3. break/continue语句的功能与执行顺序
    4. 循环条件修改后的结果判断(步长、边界值)

    注意事项

    1. 高频陷阱:无大括号时,循环仅包含紧跟的一行代码,后续变量自增/输出均不属于循环
    2. break:终止整个循环;continue:跳过本次循环后续所有代码,若出现在执行语句前,无任何输出
    3. 循环终止后,循环变量保留越界最终值(如i<11终止后i=11
    4. continue/break仅对所在的当前循环生效,不会影响外层代码

    六、数字操作与周期规律

    核心考点

    1. 整数逐位操作:取位(个位/十位/百位)、删位、数字逆序/镜面数
    2. 周期规律问题:利用取模% 解决星期/月份/数字循环问题
    3. 因数查找、数字筛选的基础逻辑

    注意事项

    1. 通用逐位操作方法:取最后一位N%10;删最后一位N/10(整型除法);取中间位(N/10^n)%10
    2. 周期规律核心:结果=(初始值+增量)%周期长度,结果为0对应周期最后一个值(如星期0=星期日,月份0=12月)
    3. 数字逆序:rst = rst*10 + N%10,高位0会自动舍弃;因数查找需注意边界(避免重复输出1)

    七、简单编程逻辑与算法

    核心考点

    1. 两个数的交换方法(临时变量法、算术法)
    2. 最大/最小值求解的逻辑与漏洞
    3. 辗转相减法(求最大公约数)、奇因数筛选
    4. 简单逻辑判断:漂亮数、27的神秘数

    注意事项

    1. 交换变量:C++不支持a,b=b,a直接交换,算术法交换需注意执行顺序(x=x+y;y=x-y;x=x-y
    2. 最值求解:初始化最值为首个输入值,需注意首次输入为结束标志的特殊情况
    3. 辗转相减法:大数减小数,直到两数相等,结果为最大公约数
    4. 因数筛选:N%i==0判断i是N的因数,i%2!=0筛选奇因数;逐位判断需结合循环N=N/10
  • 常用自带工具(函数)

    一、比较函数

    min(a, b) – 返回最小值

     #include <algorithm>
     int a = 5, b = 10;
     int smaller = min(a, b);  // 返回5

    max(a, b) – 返回最大值

     #include <algorithm>
     int a = 5, b = 10;
     int larger = max(a, b);   // 返回10

    扩展用法:

     min({a, b, c, d});      // 返回最小值
     max({a, b, c, d});      // 返回最大值

    二、交换函数

    swap(a, b) – 交换两个值

     #include <utility>  // 或 <algorithm>
     int x = 5, y = 10;
     swap(x, y);  // x=10, y=5

    三、数学函数<cmath>

    1. 绝对值

     // 整数绝对值
     int a = -5;
     int abs_a = abs(a);      
     ​
     // 浮点数绝对值
     double b = -3.14;
     double abs_b = abs(b);  

    2. 平方根 – sqrt(a)

     double x = 16.0;
     double root = sqrt(x);  // 4.0

    3. 立方根 – cbrt(a)

     double x = 27.0;
     double root = cbrt(x);  // 3.0

    4. 求幂 – pow(a, b)

     double a = 2.0, b = 3.0;
     double result = pow(a, b);  // 8.0 (2³)
     ​
     // 实际应用:
     pow(10, 2)    // 100.0
     pow(2, 0.5)   // √2 ≈ 1.414
     pow(4, -1)    // 0.25 (4⁻¹)

    四、取整函数<cmath>

    1. ceil(a) – 向上取整

     #include 
     ceil(3.2)   // 4.0
     ceil(-3.2)  // -3.0
     ceil(3.0)   // 3.0

    2. floor(a) – 向下取整

     #include <cmath>
     floor(3.8)   // 3.0
     floor(-3.8)  // -4.0
     floor(3.0)   // 3.0

    3. round(a) – 四舍五入

     #include <cmath>
     round(3.4)   // 3.0
     round(3.5)   // 4.0
     round(-3.5)  // -4.0 (中间值向远离0方向舍入)

    4. trunc(a) – 截断小数部分(向0取整)

     #include <cmath>
     trunc(3.7)   // 3.0
     trunc(-3.7)  // -3.0
     trunc(3.0)   // 3.0

    实用示例

    示例1:最大公约数和最小公倍数

     int a,b;
     cin>>a>>b;
     int p=1;
     for(int i=2;i<=min(a,b);i++){
         if(a%i==0&&b%i==0){
             p=i;
        }
     }
     cout<<p; // 最大公约数
     int q=a*b/p;
     cout<<q; // 最小公倍数

    示例2:判断质数

     int n;
     cin>>n;
     bool f=true;
     for(int i=2;i<=sqrt(n);i++){
         if(n%i==0){
             f=false;
             break;
        }
     }
     if(f){
         cout<<"yes";
     }
     else{
         cout<<"no";
     }

    示例3:求最大值

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         int a,b,c,d,e;
         cin>>a>>b>>c>>d>>e;
         cout<<max({a,b,c,d,e});
         return 0;
     }

    示例4:求a的b次方

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         int a,b;
         cin>>a>>b;
         int k=round(pow(a,b));
         cout<<k;
         return 0;
     }

    示例5:验证完全平方数

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         int n;
         cin>>n;
         int k=round(sqrt(n));
         if(k*k==n){
             cout<<"yes";
        }
         else{
             cout<<"no";
        }
         return 0;
     }
  • 格式化输入输出

    在 C++ 中,scanfprintf 是 C 语言标准输入输出库中的函数,用于格式化输入和输出。虽然在 C++ 中更常用 cincout,但这两个函数仍然广泛使用,尤其是需要高性能或格式化控制时。

    1. printf – 格式化输出

    用于将数据按指定格式输出到标准输出(通常是屏幕)。

    常用格式说明符

    • %d:整数(十进制)
    • %f:浮点数
    • %c:字符
    • %s:字符串
    • %lf:双精度浮点数(C99/C++ 中 %f 也可用于 double
    • %x:十六进制整数

    示例

     #include <iostream>
     using namespace std;
     int main() {
         int a = 42;
         double b = 3.14159;
         char c = 'A';
         const char* s = "Hello";
     ​
         printf("整数:%d\n", a);           // 整数:42
         printf("浮点数:%.2f\n", b);       // 浮点数:3.14 (保留两位小数)
         printf("字符:%c\n", c);           // 字符:A
         printf("字符串:%s\n", s);         // 字符串:Hello
         printf("十六进制:%x\n", a);       // 十六进制:2a
     ​
         return 0;
     }

    2. scanf – 格式化输入

    用于从标准输入(通常是键盘)读取数据并按照格式解析。

    注意要点

    • 传入的变量地址需要使用 & 取地址符(字符串数组名本身是地址,不需要 &
    • 返回值是成功读取的项目数,可用于错误检查

    示例

     #include <iostream>
     using namespace std;
     int main() {
         int age;
         double salary;
         char name[50];
     ​
         printf("请输入姓名、年龄和工资(用空格分隔):");
         // 注意:字符串数组 name 不需要 &
         int result = scanf("%s %d %lf", name, &age, &salary);
     ​
         if (result == 3) {
             printf("姓名:%s\n年龄:%d\n工资:%.2f\n", name, age, salary);
        } else {
             printf("输入格式错误!\n");
        }
     ​
         return 0;
     }

    对比 C++ 的 cin/cout

    特性scanf/printfcin/cout
    类型安全不安全(类型不匹配可能导致运行时错误)安全(类型在编译时检查)
    性能通常更快(尤其大量数据时)较慢(默认与 C 流同步,可关闭)
    易用性需要记忆格式符更简单直观
    扩展性不支持自定义类型(除非用 C11 扩展)支持运算符重载

    控制宽度对比

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         int a=15,b=3355,c=9;
         // 输出三个数字,用空格隔开,每个数字占8个宽度,最后换行
         cout<<setw(8)<<a<<" "<<setw(8)<<b<<" "<<setw(8)<<c<<endl;
         printf("%8d %8d %8d\n",a,b,c);
     }

    控制精度对比

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         double d=1.2345678;
         // 保留四位小数输出
         cout<<fixed<<setprecision(4)<<d;
         printf("%.4lf",d);
     }

    自动填充对比

     #include <bits/stdc++.h>
     using namespace std;
     int main(){
         int e=45;
         // 默认宽度5,不够填充0
         cout<<setw(5)<<setfill('0')<<e; // 00045
         printf("%05d",e); // 00045
     }

    输入控制巧妙结合题目

     // 输入日期,格式:y-m-d
     int y,m,d; // 年月日
     scanf("%d-%d-%d",&y,&m,&d);
     ​
     // 输入时间,格式:h:m:s
     int h,m,s; // 时分秒
     scanf("%d:%d:%d",&h,&m,&s);


    格式说明符详解

    格式:%[标记][长度][.精度]类型

    • 标记:默认右对齐
      • -代表左对齐
      • 0代表空位补零
    • 长度:代表数据至少占位的宽度
    • 精度:代表保留小数位数,需要添加.,且类型是浮点型
    • 类型:
      • int:d
      • float:f
      • double:lf
      • char:c
      • long long:lld
      • 科学计数法:e
      • 八进制:o
      • 十六进制:x

    转义符号对比

     // 用cout输出带引号的单词"hello", \"代表要输出"而非字符串两端的标记
     cout<<"\"hello\""; // "hello"
     // 用cout输出\, \\ 代表要输出\
     cout<<"\\"; // \
     // 用printf输出5%,%%代表要输出%而非匹配某个数据
     printf("%d%%",5); // 5%
  • [GESP2603一级] 数字替换

    题目信息:

    利用数字拆分将每一个数字拆分开,如果是4则换成8

    数字拆分是倒序,输出需要正序,所以需要正序拼接

    解题逻辑:

    #include <bits/stdc++.h>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        int s=0;
        for(int i=1;n>0;i=i*10){
            int t=n%10;
            if(t==4){
                s=s+8*i;
            }
            else{
                s=s+t*i;
            }
            n=n/10;
        }
        cout<<s;
        return 0;
    }
  • [GESP2512一级] 手机电量显示

    题目信息:

    经典结构:输入n,输入n个数字

    本题形容为:输入T组,每组1个数字P

    任务:根据P的数据范围,确定输出R、L、或数字

    模拟题

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int T,P;
        cin>>T;
        for(int i=1;i<=T;i++){
            cin>>P;
            if(P<=10) {
                cout<<"R"<<endl;
            }
            else if(10<=P&&P<=20){
                cout<<"L"<<endl;
            }
            else{
                cout<<P<<endl;
            }
        }
        return 0;
    }
  • [GESP2509一级] ⾦字塔

    题目信息:

    共n层,每层依次为n*n、(n-1)*(n-1)、(n-2)*(n-2)、…、2*2、1*1

    任务:统计一共需要的石块数量

    方案:累加题型,原题型累加项为i,本题累加项为i*i

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        int s=0;
        for(int i=1;i<=n;i++){
            s=s+i*i;
        }
        cout<<s;
        return 0;
    }
  • [GESP2506一级] 值日

    题目信息:

    小杨每m天一次,小红每n天一次,今天一起值日,最少多少天后,再次同一天值日

    任务:求m和n的最小公倍数

    方案:先求出m和n的最大公约数p,最小公倍数q=m*n/p

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int m,n;
        cin>>m>>n;
        int p=1; // 最大公约数
        for(int i=1;i<=m&&i<=n;i++){
            if(m%i==0&&n%i==0){
                p=i;
            }
        }
        int q=m*n/p; // 最小公倍数
        cout<<q;
        return 0;
    }
  • [GESP2503一级] 四舍五入

    题目信息:

    经典结构:输入n,输入n个数字

    任务:模拟四舍五入到十位

    方案1:判断个位数字是否大于等于5,决定进位

    方案2:直接加5,再将个位数字变成0

    解题逻辑:

    // 方案1
    #include <iostream>
    using namespace std;
    int main(){
        int n,a;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a;
            if(a%10>=5){
                a=(a/10+1)*10;
            }
            else{
                a=a/10*10;
            }
            cout<<a<<endl;
        }
        return 0;
    }
    // 方案2
    #include <iostream>
    using namespace std;
    int main(){
        int n,a;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a;
            a=a+5; // 手动四舍五入
            a=a/10*10;
            cout<<a<<endl;
        }
        return 0;
    }
  • [GESP2412一级] 奇数和偶数

    题目信息:

    经典结构:输入n,输入n个数字

    任务:统计奇数和偶数的数量

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int n,a;
        cin>>n;
        int j=0,o=0;
        for(int i=1;i<=n;i++){
            cin>>a;
            if(a%2!=0){
                j++;
            }
            else{
                o++;
            }
        }
        cout<<j<<" "<<o;
        return 0;
    }
  • [GESP2409一级] 美丽数字

    题目信息:

    经典结构:输入n,输入n个数字

    任务:统计美丽数字的数量

    美丽数字规则:是9的倍数并且不是8的倍数

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int n,a,c=0;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a;
            if(a%9==0&&a%8!=0){
                c++;
            }
        }
        cout<<c;
        return 0;
    }
  • [GESP2406一级] 立方数

    题目信息:

    立方数概念:如果存在一个数字x,满足x^3的值恰好是n,则n是立方数

    循环范围:x从1开始,直到x*x*x超过结束

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        for(int x=1;x*x*x<=n;x++){
            if(x*x*x==n){
                cout<<"Yes";
                return 0; // 输出答案,直接结束
            }
        }
        cout<<"No";
        return 0;
    }
  • [GESP2403一级] 找因数

    题目信息:

    循环范围:从1到a

    输出要求:能整除a的所有正整数(因数)

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int a;
        cin>>a;
        for(int i=1;i<=a;i++){
            if(a%i==0){
                cout<<i<<endl;
            }
        }
        return 0;
    }
  • [GESP2312一级] 小杨报数

    题目信息:

    循环范围:从1到N

    报数规则:跳过M的倍数

    跳过:continue

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int N,M;
        cin>>N>>M;
        for(int i=1;i<=N;i++){
            if(i%M==0) continue;
            cout<<i<<endl;
        }
        return 0;
    }
  • [GESP2309一级] 小明的幸运数

    题目信息:

    循环范围:从L到R(包含L和R)

    幸运数规律:1、个位为k,2、k的倍数

    求幸运数的和

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int k,L,R;
        cin>>k>>L>>R;
        int s=0;
        for(int i=L;i<=R;i++){
            if(i%10==k||i%k==0){
                s=s+i;
            }
        }
        cout<<s;
        return 0;
    }
  • [GESP2306一级] 累计相加

    题目信息:

    阶乘和的变种,累加和

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        int sum=0;
        for(int i=1;i<=n;i++){
            int s=0;
            for(int j=1;j<=i;j++){
                s=s+j;
            }
            sum=sum+s;
        }
        cout<<sum;
        return 0;
    }
    // 简化方案
    #include <iostream>
    using namespace std;
    int main(){
        int n;
        cin>>n;
        int sum=0,s=0;
        for(int i=1;i<=n;i++){
            s=s+i;
            sum=sum+s;
        }
        cout<<sum;
        return 0;
    }
  • [GESP2303一级] 长方形面积

    题目信息:

    a为长方形面积,c为统计长宽组合数

    计算过程中,需要注意长1宽6长6宽1是同一种长方形,并且正方形也属于长方形

    解题逻辑:

    #include <iostream>
    using namespace std;
    int main(){
        int c=0,a;
        cin>>a;
        // i*i<=a既能包含正方形,又能避免重复数据
        for(int i=1;i*i<=a;i++){
            // 能整除,符合长方形长宽整数设定
            if(a%i==0){
                c++;
            }
        }
        cout<<c;
        return 0;
    }
  • 启蒙阶段常用算法技巧(续)

    判断质数

    判断是否是质数的代价不平衡,则先假设难判断的情况为真

    • 是质数:从2到n-1所有数字都不能整除(困难)
    • 不是质数:从2到n-1存在能整除的数(容易)
    • 先假设是质数,再反证不是质数
    int n;
    cin>>n;
    if(n<2){
        cout<<"no"; // 排除特殊情况
        return 0;
    }
    bool f=true; // 创建一个标记,假设n是质数
    for(int i=2;i<n;i++) {
       if(n%i==0){
           f=false; // 发现整除,修改标记
           break;
       } 
    }
    if(f){
        cout<<"yes";
    }
    else{
        cout<<"no";
    }
    return 0;

    分解质因数

    从2开始枚举所有质数,判断是否能整除n

    • 如果能整除,改变n值重新判断
    • 如果不能整除,除数递增
    • 直到n变为1
    int n;
    cin>>n;
    if(n<2){
        cout<<n; // 排除特殊情况
        return 0;
    }
    int t=2;
    while(n!=1){
        while(n%t==0){
            cout<<t<<" ";
            n=n/t;
        }
        t++;
    }

    水仙花数

    水仙花数是指一个3位数,其各位数字的3次幂之和等于它本身的数字。

    • 先把每个数字拆分成个位、十位、百位
    • 再利用水仙花数特点公式判断是否符合要求
    for(int i=100;i<=999;i++){
        int g=i%10;
        int s=i/10%10;
        int b=i/100;
        if(g*g*g+s*s*s+b*b*b == i){
            cout<<i<<" ";
        }
    }

    阶乘和

    先计算出n的阶乘,再列出前n项,每个n的阶乘,最后做累加

    int n;
    cin>>n;
    // 计算出n的阶乘
    int s=1;
    for(int i=1;i<=n;i++){
        s=s*i; 
    }
    cout<<s; // n的阶乘
    int n;
    cin>>n;
    // 计算出每个1~n之间的数的阶乘
    for(int t=1;t<=n;t++){
        int s=1;
        for(int i=1;i<=t;i++){
            s=s*i; 
        }
        cout<<s<<" "; // t的阶乘
    }
    // 最终求和
    int n;
    cin>>n;
    int ans=0; // 累加初始值
    for(int t=1;t<=n;t++){
        // 计算出n的阶乘
        int s=1;
        for(int i=1;i<=t;i++){
            s=s*i; 
        }
        ans=ans+s; // 累加各个阶乘值
    }
    cout<<ans;
    // 进阶版本
    int n;
    cin>>n;
    int ans=0; // 累加初始值
    int s=1;
    for(int t=1;t<=n;t++){
        s=s*t; // 阶乘
        ans=ans+s; // 阶乘和
    }
    cout<<ans;

    星阵问题

    输入n,输出n*n的特定星阵图

    int n;
    cin>>n;
    for(int i=1;i<=n;i++){ // 外层循环确定打印几行
        for(int j=1;j<=i;j++){ // 内层循环确定每行打印的内容
            cout<<"*"; // 根据条件确定具体细节
        }
        cout<<endl;
    }

    常用星阵图像条件

    • 对角线:i==j||i+j==n+1
    • 上下边界:i==1||i==n
    • 左右边界:j==1||j==n

  • 启蒙阶段常用算法技巧

    常用判断条件

    基本比较

    // 等于(注意与赋值=的区别)
    if (a == b)  // 等于
    if (a != b)  // 不等于
    if (a > b)   // 大于
    if (a >= b)  // 大于等于
    if (a < b)   // 小于
    if (a <= b)  // 小于等于

    范围判断

    // 判断在区间内
    if (x >= 0 && x <= 100)     // [0, 100]闭区间
    if (x > 0 && x < 100)       // (0, 100)开区间
    if (score >= 60 && score < 90)  // [60, 90)
    
    // 判断在区间外
    if (x < 0 || x > 100)       // (-∞, 0) ∪ (100, +∞)
    if (!(x >= 0 && x <= 100))  // 等价于上一行
    
    // 多个条件组合
    if ((age >= 18 && age <= 60) && (height >= 160))

    取余运算应用

    if (n % 2 == 0)  // 偶数
    if (n % 2 != 0)  // 奇数
    if (n % 2 == 1)  // 正奇数(负数情况要注意)

    位运算应用(高阶用法、仅了解)

    if ((n & 1) == 0)  // 偶数(二进制末位为0)
    if (n & 1)         // 奇数(二进制末位为1)

    整除判断

    if (n % 3 == 0)      // 能被3整除
    if (n % 5 != 0)      // 不能被5整除
    if (n % 2 == 0 && n % 3 == 0)  // 同时被2和3整除(6的倍数)
    if (n % 4 == 0 || n % 6 == 0)  // 能被4或6整除

    特殊数值判断

    // 零值判断
    if (x == 0)
    if (x != 0)
    
    // 正负判断
    if (x > 0)    // 正数
    if (x < 0)    // 负数
    if (x >= 0)   // 非负数
    if (x <= 0)   // 非正数
    
    // 特定值判断
    if (x == 100)       // 等于100
    if (x != -1)        // 不等于-1(常用于结束标志)
    if (abs(x) == 1)    // 绝对值为1 abs()是自带绝对值函数

    字符判断

    #include <cctype>  // 包含字符判断函数
    
    // 大小写字母
    if (ch >= 'A' && ch <= 'Z')    // 大写字母
    if (ch >= 'a' && ch <= 'z')    // 小写字母
    if (isupper(ch))               // 标准库函数判断大写
    if (islower(ch))               // 标准库函数判断小写
    
    // 数字字符
    if (ch >= '0' && ch <= '9')    // 数字字符
    if (isdigit(ch))               // 标准库函数判断数字
    
    // 其他字符
    if (ch == ' ' || ch == '\t' || ch == '\n')  // 空白字符
    if (isspace(ch))                            // 标准库函数判断空白
    if (ch == '+' || ch == '-' || ch == '*' || ch == '/')  // 运算符

    与或非运算

    与运算(&&) - 所有条件都要满足
    if (age >= 18 && score >= 60)      // 成年且及格
    if (a > 0 && b > 0 && c > 0)       // 三个数都为正
    或运算(||) - 至少一个条件满足
    if (score < 60 || score > 90)      // 不及格或优秀
    if (ch == 'Y' || ch == 'y')        // 大小写Y都接受
    非运算(!) - 条件取反
    if (!(score >= 60))                // 等价于 score < 60
    if (!isFinished)                   // 未完成

    混合使用注意优先级

    // && 优先级高于 ||
    if (age >= 18 && (gender == 'M' || gender == 'F'))  // 正确
    if (age >= 18 && gender == 'M' || gender == 'F')    // 可能错误
    
    // 括号明确优先级
    if ((score >= 90 && attendance > 0.8) || isSpecial) 

    三角形构成条件

    if (a + b > c && a + c > b && b + c > a)  // 能构成三角形

    闰年判断

    if ((year%4==0 && year%100!=0) || (year%400==0))

    三数排序/比较

    // 找最大值
    if (a >= b && a >= c) max = a;
    else if (b >= a && b >= c) max = b;
    else max = c;
    
    // 判断是否升序
    if (a <= b && b <= c)  // 升序排列

    相等判断技巧

    // 三数相等
    if (a == b && b == c)
    
    // 两数相等,第三数不同
    if (a == b && b != c)
    
    // 至少有两个数相等
    if (a == b || b == c || a == c)

    赋值与比较

    if (x = 5)    // 错误!这是赋值,此时总是为真,因为5就是true
    if (x == 5)   // 正确!比较

    浮点数比较

    double a = 0.1 + 0.2;
    if (a == 0.3)          // 错误!浮点数精度问题
    if (abs(a - 0.3) < 1e-6)  // 正确!允许微小误差

    交换变量的值

    使用中间变量(最常用)

    #include <iostream>
    using namespace std;
    
    int main() {
        int a = 10, b = 20;
        
        // 使用临时变量交换
        int temp = a;
        a = b;
        b = temp;
        
        cout << "a = " << a << ", b = " << b << endl;
        return 0;
    }

    使用加减法(无临时变量)

    a = a + b;    // a现在保存和
    b = a - b;    // b得到原来的a值
    a = a - b;    // a得到原来的b值

    使用异或运算(无临时变量)

    a = a ^ b;    // a = a XOR b
    b = a ^ b;    // b = (a XOR b) XOR b = a
    a = a ^ b;    // a = (a XOR b) XOR a = b

    使用标准库函数 swap(最推荐)

    #include <iostream>
    #include <utility>  // 或 <algorithm>(旧版本)
    using namespace std;
    int main() {
        int x = 5, y = 7;
        
        swap(x, y);  // 推荐使用
        
        cout << "x = " << x << ", y = " << y << endl;
        return 0;
    }

    数据拆分与回文数

    固定长度数字拆分(已知位数)

    // 方法1:数学计算法
    int num = 123;
    int hundred = num / 100;      // 百位:123/100 = 1
    int ten = num / 10 % 10;      // 十位:123/10=12, 12%10=2
    int unit = num % 10;          // 个位:123%10 = 3
    
    // 方法2:直接取余法
    hundred = num / 100;          // 百位
    ten = num % 100 / 10;         // 十位:123%100=23, 23/10=2
    unit = num % 10;              // 个位

    非固定长度数字拆分(未知位数)

    正向拆分(从个位到最高位)

    int num = 12345;
    int digit;
    
    // 边拆分边处理
    while (num > 0) {
        digit = num % 10;     // 取最后一位
        cout << digit << " "; // 输出:5 4 3 2 1
        num /= 10;            // 去掉最后一位
    }

    反向拆分(从最高位到个位)

    int num = 12345;
    int temp = num;
    int divisor = 1;
    
    // 先找到最高位的权值
    while (temp >= 10) {
        divisor *= 10;      // 最终divisor=10000
        temp /= 10;
    }
    
    // 从最高位开始拆分
    temp = num;
    while (divisor > 0) {
        int digit = temp / divisor;    // 取当前最高位
        cout << digit << " ";          // 输出:1 2 3 4 5
        temp %= divisor;               // 去掉最高位
        divisor /= 10;                 // 权值减少10倍
    }

    回文数判断

    回文数指正反读一样的数字,如12321、1221、5、0

    反转数字法(最常用)

    int original = num; // 备份
    int reversed = 0;
    while (num > 0) {
        reversed = reversed * 10 + num % 10;  // 反转拼接
        num /= 10;
    }
        
    if (reversed == original) {
        cout << "yes";
    }
    else {
        cout << "no";
    }

    累加累乘问题

    固定解题步骤:1、确定循环范围;2、确定初始值;3、确定累加或累乘项

    求结果

    #include <iostream>
    using namespace std;
    int main(){
        int n,s=0; // 累乘s=1
        cin>>n;
        for(int i=1;i<=n;i++){
            s=s+i; // 累乘s=s*i
        }
        cout<<s;
        return 0;
    }

    求上限的项

    #include <iostream>
    using namespace std;
    int main(){
        int n=1,s=0,k; // 累乘s=1
        cin>>k;
        while(true){
            s=s+n; // 累乘s=s+n
            if(s>k) break;
            n++;
        }
        cout<<n;
        return 0;
    }

    求最值问题

    原则:求最大设最小、求最小设最大

    #include <iostream>
    #include <climits>
    using namespace std;
    int main(){
        int n,a;
        cin>>n;
        int maxa=INT_MIN,mina=INT_MAX;
        for(int i=1;i<=n;i++){
            cin>>a;
            if(maxa<a){
                maxa=a;
            }
            if(mina>a){
                mina=a;
            }
        }
        cout<<maxa<<" "<<mina;
        return 0;
    }
  • 其他程序结构

    switch分支结构语句

    基本语法

    switch(被选变量) {
        case 值1:
            // 代码块1
            break;
        case 值2:
            // 代码块2
            break;
        case 值3:
            // 代码块3
            break;
        default:
            // 默认代码块
    }

    特点

    • 被选变量必须是整型或枚举类型(能够准确判断相等的类型)
    • case后面必须是常量表达式(确定的值)
    • break可以不写,本质上break用于跳出switch结构,不写break会继续执行下一个case中的代码(穿透执行)
    • default是可有可无,当所有case都不匹配时执行,类似else

    if与switch的区别

    对比维度if-else ifswitch
    条件类型任意布尔表达式(可复杂)只能整型、字符、枚举
    比较方式相等、不等、大小比较只能相等比较
    执行方式顺序判断,找到真为止直接跳转到匹配的 case
    性能O(n) 时间,n 是分支数编译器可优化为 O(1) 跳转表
    适用场景条件复杂、有范围判断固定值匹配、分支多
    可读性条件复杂时清晰分支多且固定值时清晰

    while循环结构语句

    基本语法

    while(条件) {
        // 循环体
        // 需要改变 条件相关变量,否则可能无限循环
    }

    执行流程

    1. 检查条件是否为真
    2. 如果条件为真,执行循环体(通常循环体中要有更新条件变量的功能)
    3. 每次执行完循环体,再次检查条件,直到条件为假

    示例

    // 示例1:基本用法
    int i = 1;
    while(i <= 5) {
        cout << i << " ";
        i++;  // 重要:更新条件变量
    }
    // 输出:1 2 3 4 5
    
    // 示例2:无限循环(通常配合 break)
    int count = 0;
    while(true) {
        count++;
        if(count >= 10) {
            break;  // 跳出循环
        }
    }
    
    // 示例3:输入验证
    int num;
    cout << "请输入1-10的数:";
    cin >> num;
    while(num < 1 || num > 10) {
        cout << "输入错误,重新输入:";
        cin >> num;
    }

    do-while循环结构语句

    基本语法

    do {
        // 循环体
        // 至少执行一次
    } while(条件);  // 注意分号!

    特点

    • 先执行,后判断:循环体至少会执行一次
    • 适用于需要先执行再检查的情况
    • 末尾必须有分号

    for、while与do-while的区别

    循环类型执行次数检查时机适用场景
    for0次或多次先检查,后执行已知循环次数、遍历
    while0次或多次先检查,后执行条件控制、可能不执行
    do-while至少1次先执行,后检查必须至少执行一次

    理论上,在任何情况下,for和while都能够相互替换,do-while不行

  • 循环结构

    for循环的基本结构

    for (初始化; 条件; 更新) {
        // 要重复执行的代码
    }
    1. 初始化:循环正式开始前先执行,只执行一次
    2. 条件:每次进入循环前检查,结果为真则执行循环内容
    3. 更新:每次循环内容执行结束后执行,执行完回到“条件”位置

    使用案例:打印数字0~4

    #include <iostream>
    using namespace std;
    
    int main() {
        for (int i = 0; i < 5; i++) {
            cout << i << endl;
        }
        return 0;
    }

    执行顺序:

    1. int i = 0 (初始化,只执行一次)
    2. 检查 i < 5 (0 < 5,为真)
    3. 执行 cout << i << endl; (输出 0 换行)
    4. 执行 i++ (i变为1)
    5. 检查 i < 5 (1 < 5,为真)
    6. 执行 cout << i << endl; (输出 1 换行)
    7. … 重复直到条件为假

    for循环的常见用法

    • 计数循环(常用)
      // 打印1-10
      for (int i = 1; i <= 10; i++) {
          cout << i << " ";
      }
      // 输出:1 2 3 4 5 6 7 8 9 10
      • 倒序循环
      // 倒计时:5 4 3 2 1
      for (int i = 5; i >= 1; i--) {
          cout << i << " ";
      }
      • 步长不为1的循环
      // 打印0-10之间的偶数
      for (int i = 0; i <= 10; i += 2) {
          cout << i << " ";
      }
      // 输出:0 2 4 6 8 10

      实际应用示例

      示例1:计算1-100的和

      int sum = 0;
      for (int i = 1; i <= 100; i++) {
          sum += i;  // 等价于 sum = sum + i
      }
      cout << "1到100的和是:" << sum << endl;

      示例2:找n个正整数中的最大值

      int n,a;
      cin >> n;
      int max = -1;
      for (int i = 1; i <= n; i++) {
          cin >> a;
          if(max < a){
              max = a;
          }
      }
      cout << "最大值是:" << max << endl;

      控制循环的关键字

      • break 立即结束整个循环
      // 找到第一个负数就停止
      int num;
      for (int i = 0; i < 6; i++) {
          cin >> num; // 5 3 -2 7 -1 4
          if (num < 0) {
              cout << num << " ";
              break;  // 立即退出循环
          }
      }
      //输出:5 3
      • continue 跳过本次循环
      // 只打印正数,跳过负数
      int num;
      for (int i = 0; i < 5; i++) {
          cin >> num; // 5 -3 2 -7 1
          if (num < 0) {
              continue;  // 跳过负数,继续下一次循环
          }
          cout << num << " ";
      }
      // 输出:5 2 1

      常见错误

      • 死循环错误
      // ❌ 忘记更新变量
      for (int i = 0; i < 5; ) {  // 缺少 i++
          cout << i << endl;
          // 会一直输出0,永远停不下来!
      }
      • 分号位置错误
      // ❌ 错误:在for后面加多余的分号
      for (int i = 0; i < 5; i++);  // 这个分号结束了循环
      {
          cout << i << endl;  // 这行只执行一次,而且i的作用域可能有问题
      }
      
      // ✅ 正确:没有多余分号
      for (int i = 0; i < 5; i++) {
          cout << i << endl;  // 这会执行5次
      }
      • 作用域问题
      for (int i = 0; i < 5; i++) {
          // i在这里可用
      }
      // i在这里不可用(超出作用域)
      cout << i << endl;  // ❌ 编译错误
      • 与if语句类似,for循环的{}中只有一句时也能省略大括号,新手不要这样做
    1. [GESP2603一级] 交朋友

      题目信息:

      目的:要选出与h1身高最接近的、较矮的一方

      矮个优先原则,先将h2、h3、h4从小到大排序

      依次计算身高差,并保存最小身高差对应的身高答案

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int h1,h2,h3,h4;
          cin>>h1>>h2>>h3>>h4;
          // 先确定 h2<h3<h4 从矮到高
          if(h2>h3) {
              int t=h2;
              h2=h3;
              h3=t;
          }
          if(h2>h4) {
              int t=h2;
              h2=h4;
              h4=t;
          }
          if(h3>h4) {
              int t=h3;
              h3=h4;
              h4=t;
          }
          int c=h1-h2,ans=h2; // 假设答案是h2,c为高度差
          if(c<0) c=0-c; // 保证身高差为正数
          int d=h1-h3,e=h1-h4; // 计算另外两名同学身高差
          if(d<0) d=0-d;
          if(e<0) e=0-e;
          if(d<c){
              ans=h3;
              c=d;
          }
          if(e<c){
              ans=h4;
              c=e;
          }
          cout<<ans;
          return 0;
      }
    2. [GESP2512一级] 小杨的爱心快递

      题目信息:

      变量含义:体积V,重量G

      体积计算:0.5 * V

      重量计算:M (G<300),N (G>=300)

      取价格较低

      解题逻辑:

      #include <bits/stdc++.h> 
      using namespace std;
      int main(){
          double V,G,M,N;
          cin>>V>>G>>M>>N;
          double p1=0.5*V;
          double p2=M; // 假设重量小于300
          if(G>=300){
              p2=N; // 如果重量大于等于300,将价格修改成N
          }
          if(p1<p2){
              cout<<fixed<<setprecision(1)<<p1;
          }
          else{
              cout<<fixed<<setprecision(1)<<p2;
          }
          return 0;
      }
    3. [GESP2509一级] 商店折扣

      题目信息:

      折扣方案1:满 x 元减 y 元(仅用一次)

      折扣方案2:打 n 折(原价*n/10

      原总价为 p 元,求最少花费

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          double x,y,n,p;
          cin>>x>>y>>n>>p;
          double p1=p,p2=p;
          if(p>=x){ // 如果符合满x减y的情况
              p1=p-y;
          }
          p2=p*n/10;
          cout<<fixed<<setprecision(2);
          if(p1<p2){
              cout<<p1;
          }
          else{
              cout<<p2;
          }
          return 0;
      }
    4. [GESP2506一级] 假期阅读

      题目信息:

      变量含义:共 n 页,每天最多阅读 k 页,假期最多读 t 天

      共阅读:k*t页书

      特别的:如果读完所有页,剩余天数不会读更多书

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,k,t;
          cin>>n>>k>>t;
          int x=k*t; // 最多读x页
          if(x>n){
              cout<<n; // 最多有n页
          }
          else{
              cout<<x;
          }
          return 0;
      }
    5. [GESP2503一级] 图书馆里的老鼠

      题目信息:

      变量含义:共有n本书,每x小时啃一本,一共啃y小时

      求剩下完整的书,则即使没啃完,当前这本书,也算啃坏

      特别的,如果啃食时间过长,所有书都啃光还有时间,则不会变成负数书

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,x,y;
          cin>>n>>x>>y;
          int t=y/x; // 啃没了多少本书
          if(y%x!=0){
              t=t+1; // 还能再啃坏一本
          }
          if(t<n){
              cout<<n-t; // 剩余完好
          }
          else{
              cout<<0; // 所有书籍被啃光
          }
          return 0;
      }
    6. [GESP2412一级] 温度转换

      题目信息:

      变量含义:开尔文温度k

      公式1:C=K-273.15

      公式2:F=C*1.8+32

      输入开尔文温度k,求摄氏温度C和华氏温度F,如果F过高,输出提示

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          double k,c,f;
          cin>>k;
          c=k-273.15;
          f=c*1.8+32;
          cout<<fixed<<setprecision(2);
          if(f>212){
              cout<<"Temperature is too high!";
          }
          else{
              cout<<c<<" "<<f;
          }
          return 0;
      }
    7. [GESP2409一级] 小杨购物

      题目信息:

      变量含义:共n元,A商品a元,B商品b元

      购买相同数量的A、B商品(可以把A、B商品设为一个整体商品X,价格为a+b元)

      计算最多购买多少个X商品

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,a,b;
          cin>>n>>a>>b;
          int x=a+b;
          cout<<n/x;
          return 0;
      }
    8. [GESP2406一级] 休息时间

      题目信息:

      变量含义:h时,m分,s秒,k学习总秒数

      求休息时分秒

      方案:模拟60进制,以此计算多出的分,进位,多出的时,进位

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int h,m,s,k;
          cin>>h>>m>>s>>k;
          s=s+k;
          m=m+s/60; // 计算多出的分,进位
          s=s%60; // 保留剩余秒数
          h=h+m/60; // 计算多出的时,进位
          m=m%60; // 保留剩余分钟
          cout<<h<<" "<<m<<" "<<s;
          return 0;
      }
    9. [GESP2403一级] 小杨买书

      题目信息:

      变量含义:m为钱总数

      被除数 ➗ 除数 🟰 商 …. 余数

      商为最多购买的数量,余数为剩余零用钱

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int m;
          cin>>m;
          cout<<m/13<<endl<<m%13;
          return 0;
      }
    10. [GESP2312一级] 小杨的考试

      题目信息:

      变量含义:今天星期X,N天后考试

      计算星期:对 7 取余,结果可能是0、1、2、3、4、5、6

      假设X为一,则1对应星期一,2对应星期二,3对应星期三,4对应星期四,5对应星期五,6对应星期六,0对应星期日,

      计算(X+N)%7即可

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int X,N;
          cin>>X>>N;
          int ans=(X+N)%7;
          if(ans==0) {
              ans=7; // 0对应星期日
          }
          cout<<ans;
          return 0;
      }
    11. [GESP2309一级] 买文具

      题目信息:

      签字笔单价2元,记事本单价5元,直尺单价3元

      变量含义:笔X支,本Y本,尺Z把,有Q元钱

      计算剩余钱数:s=Q-2*X-5*Y-3*Z

      如果s小于0,No,输出s的绝对值(缺少的钱数)

      否则,Yes,输出s

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int X,Y,Z,Q;
          cin>>X>>Y>>Z>>Q;
          int s=Q-2*X-5*Y-3*Z;
          if(s<0){
              cout<<"No"<<endl<<-s;
          }
          else{
              cout<<"Yes"<<endl<<s;
          }
          return 0;
      }
    12. [GESP2306一级] 时间规划

      题目信息:

      变量含义:开始时sh,开始分sm,结束时eh,结束分em

      将开始时间和结束时间全转换成分钟(1时=60分)

      相减

      解题逻辑:

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int sh,sm,eh,em;
          cin>>sh>>sm>>eh>>em;
          int st=sh*60+sm;
          int et=eh*60+em;
          cout<<et-st;
          return 0;
      }
    13. [GESP2303一级] 每月天数

      题目信息:

      变量含义:年y、月m

      判断平闰年条件:y%400==0||y%4==0&&y%100!=0

      1、3、5、7、8、10、12月31天

      4、6、9、11月30天

      2月平年28天,闰年29天

      解题逻辑:

       #include <bits/stdc++.h>
       using namespace std;
       int main(){
           int y,m;
           cin>>y>>m;
           if(m==2){
               if(y%400==0||y%4==0&&y%100!=0){
                   cout<<29;
               }
               else{
                   cout<<28;
               }
           }
           else if(m==4||m==6||m==9||m==11){
               cout<<30;
           }
           else{
               cout<<31;
           }
           return 0;
       }
    14. 关系运算符

      运算符名称含义示例结果
      ==等于判断两个值是否相等5 == 5true
      !=不等于判断两个值是否不相等5 != 3true
      >大于判断左侧值是否大于右侧值10 > 5true
      <小于判断左侧值是否小于右侧值3 < 7true
      >=大于等于判断左侧值是否大于或等于右侧值5 >= 5true
      <=小于等于判断左侧值是否小于或等于右侧值4 <= 4true

      使用实例:相等运算符==和!=

      int a = 10, b = 20, c = 10;
      
      cout << (a == b) << endl;   // 输出: 0 (false)
      cout << (a == c) << endl;   // 输出: 1 (true)
      cout << (a != b) << endl;   // 输出: 1 (true)
      cout << (a != c) << endl;   // 输出: 0 (false)
      
      // 也可以用于字符比较
      char ch1 = 'A', ch2 = 'B';
      cout << (ch1 == ch2) << endl;  // 输出: 0 (false)
      
      // 字符串比较(需要特殊处理,后面会讲)
      string s1 = "hello", s2 = "world";
      cout << (s1 == s2) << endl;    // 输出: 0 (false)

      使用实例:大小比较运算符>、<、>=和<=

      int x = 15, y = 20, z = 15;
      
      cout << (x > y) << endl;    // 15 > 20? false → 0
      cout << (x < y) << endl;    // 15 < 20? true → 1
      cout << (x >= z) << endl;   // 15 >= 15? true → 1
      cout << (y <= z) << endl;   // 20 <= 15? false → 0
      
      // 浮点数比较(需要小心精度问题)
      double d1 = 1.0, d2 = 0.999999999;
      cout << (d1 > d2) << endl;  // 输出: 1 (true)

      优先级

      关系运算符的优先级低于算术运算符,但高于赋值运算符:

      int a = 5, b = 3, c = 2;
      
      // 算术运算先执行,然后比较
      bool result = a + b > c * 3;  // 等价于 (5+3) > (2*3) → 8 > 6 → true
      cout << result << endl;       // 输出: 1
      
      // 优先级示例
      int x = 10, y = 5, z = 3;
      bool r1 = x + y > z * 2;      // 等价于 (10+5) > (3*2) → 15 > 6 → true
      bool r2 = x > y + z;          // 等价于 10 > (5+3) → 10 > 8 → true

    15. 逻辑运算符与分支结构

      逻辑运算符

      运算结果为布尔型数据(只有true和false)

      运算符名称含义示例(结果)
      !逻辑非取反。如果操作数为 true,则结果为 false,反之亦然。!truefalse
      &&逻辑与仅当两个操作数都为 true 时,结果才为 truetrue && falsefalse
      ||逻辑或只要任意一个操作数为 true,结果就为 truetrue || falsetrue

      真值表

      条件A条件B!条件A条件A && 条件B条件A || 条件B
      truetruefalsetruetrue
      truefalsefalsefalsetrue
      falsetruetruefalsetrue
      falsefalsetruefalsefalse

      相关特性与概念

      1. 短路求值
        • &&(逻辑与):如果左边的操作数结果为 false,则整个表达式立刻确定为 false右边的操作数根本不会被执行或计算
        • ||(逻辑或):如果左边的操作数结果为 true,则整个表达式立刻确定为 true右边的操作数不会被计算
      2. 操作数与返回值
        • 操作数: 通常是结果为 bool 类型的表达式(如 a > bflag)。C++ 中,任何非零值在逻辑上下文中都被视为 true零值被视为 false
        • 返回值: 逻辑运算符的结果是 bool 类型,即 truefalse
      3. 运算符优先级:在复杂表达式中,逻辑非>逻辑与>逻辑或
      bool result = !true || false && true;
      // 等价于:((!true) || (false && true)) 

      分支结构:if语句

      单分支句式

      if (条件表达式) {
          // 当条件为真时执行的代码块
      }
      • 计算条件表达式的值
      • 如果值为true(非0),执行{}中的代码
      • 如果值为false(0),跳过{}中的代码

      双分支句式

      if (条件表达式) {
          // 条件为真时执行的代码块
      } else {
          // 条件为假时执行的代码块
      }
      • 计算条件表达式的值,有选择的执行两个{}代码中的一个

      多分支句式

      if (条件1) {
          // 条件1为真时执行
      } else if (条件2) {
          // 条件1为假,但条件2为真时执行
      } else if (条件3) {
          // 条件1、2为假,但条件3为真时执行
      } else {
          // 所有条件都为假时执行
      }
      • 从上至下计算每一个条件,直到结果为真,执行其中一个代码

      大括号{}用法

      • 当条件对应的执行内容只有一句时,可以省略大括号,但对于新手,强烈建议始终使用大括号,即使只有一行代码
      • 没有大括号时,只控制紧随其后的一句代码
      // 正确但不推荐
      if (x > 0)
          cout << "正数";
      else
          cout << "非正数";
      
      // 推荐写法 - 更清晰,更安全
      if (x > 0) {
          cout << "正数";
      } else {
          cout << "非正数";
      }

      嵌套条件结构

      结构之间可以相互嵌套

      int age = 20;
      bool hasLicense = true;
      
      if (age >= 18) {
          cout << "满足年龄要求" << endl;
          if (hasLicense) {
              cout << "可以合法驾驶" << endl;
          } else {
              cout << "但需要考取驾照" << endl;
          }
      } else {
          cout << "未满18岁,不能驾驶" << endl;
      }

      常见错误

      • 赋值运算符(=)当比较运算符(==)用
      // 错误:这是赋值,不是比较!
      if (x = 5) {  // 总是为真,因为 x=5 返回 5(非零)
          cout << "x等于5";
      }
      
      // 正确
      if (x == 5) {
          cout << "x等于5";
      }
      • 悬空else问题
      // 哪个if对应哪个else?容易混淆
      if (con1)
          if (con2)
              statement1;
      else  // 这个else实际属于最近的if(con2),而不是if(con1)
          
      // 解决方法:使用大括号明确范围
      if (con1) {
          if (con2) {
              statement1;
          } else {
              statement2;
          }
      }
      • 忽略边界问题
      // 错误:99分也会被判断为B
      if (score >= 90) grade = 'A';
      if (score >= 80) grade = 'B';  // 这里应该用 else if

      房老师建议

      1. 始终使用大括号 {}
      2. 保持条件简单清晰,复杂逻辑拆分或使用变量
      3. 使用有意义的布尔变量名
      4. 注意条件的排列顺序,将最常见或最可能的情况放在前面
      5. 避免过深的嵌套(一般不超过3层)

      条件运算符(三目运算符或称三元运算符)

      作为if-else双分支结构的简洁替代方式

      // 标准if-else
      int max;
      if (a > b) {
          max = a;
      } else {
          max = b;
      }
      
      // 使用三元运算符
      int max = (a > b) ? a : b;
      
      // 更复杂的例子
      string result = (score >= 60) ? "及格" : "不及格";
      cout << "考试结果: " << result << endl;
    16. 计算机网络

      计算机网络是指将地理位置不同的、具有独立功能的多个计算机及其外部设备,通过通信线路和网络设备连接起来,在网络操作系统、管理软件及通信协议的管理和协调下,实现资源共享和信息传递的系统。

      一、核心组成

      1. 硬件设备
        • 端设备(筑基):个人电脑、手机、服务器等
        • 中间设备:网口、交换机、路由器、无线接入点等
        • 传输介质:网线、wifi、光纤、无线电波、蓝牙、4G/5G等
      2. 软件与协议
        • 网络操作系统:Windows Server、Linux、CentOS等
        • 网络协议:TCP/IP协议、HTTP/HTTPS协议、DNS协议等
      3. 服务和数据
        • 网络存在的意义是提供服务,如web服务、电子邮件、文件共享、视频流等
        • 数据在网络上以特定的格式单元进行传输,如数据包、帧

      二、主要分类

      1. 按地理范围:局域网(LAN)、城域网(MAN)、广域网(WAN)、个人局域网(PAN)、互联网(Internet)
      2. 按拓扑结构:总线型、星型、环型、网型、混合型
      3. 按管理模式:
        • 客户端-服务端:有专用服务器提供服务,客户端请求服务(网站访问)
        • 对等网络:所有设备地位平等(文件共享)

      三、关键技术

      1. 体系结构:将复杂的网络通信过程分层,每一层油明确的功能和协议。
        • OSI七层模型(理论标准):应用层、表示层、会话层、传输层、网络层、数据链路层、物理层
        • TCP/IP四层模型(实际应用):应用层、传输层、网络层、网络接口层
      2. IP地址与子网划分:用于高效管理和分配IP地址。
        • IPv4(如:192.168.1.1)
        • IPv6(如:2001:0db8::1)
      3. 路由与交换:
        • 交换:在局域网中,基于MAC地址进行高速数据转发
        • 路由:在不同网络间,通过路由协议(如OSPF、BGP)动态学习路径,基于IP地址进行转发
      4. 无线技术:WI-FI(IEEE802.11系列)、蜂窝网络(4GLTE、5G)、蓝牙、ZigBee等
      5. 虚拟化与软件定义网络:
        • VPN:在公共网络上建立加密的私有通道
        • SDN:奖网络控制层与数据转发层分离,实现网络的灵活编程和集中管理
        • 网络虚拟化:在一套物理设备上虚拟出多个独立的逻辑网络

      四、网络病毒与恶意软件

      寄生在正常的“数字宿主”(文件或程序)中,伺机复制和传播,并对计算机系统造成各种危害。

      计算机病毒是一种恶意软件,它有两个最根本的特征:

      1. 寄生性: 必须“依附”在一个正常的计算机程序或文件上(如 .exe 可执行文件、Word文档、脚本文件)。
      2. 传染性: 能够自我复制,将其代码插入其他程序或文件中,从而感染它们。

      这与独立运行的蠕虫、木马有所不同(虽然普通人常统称它们为“病毒”)。

      病毒需要“搭便车”或“被激活”,主要途径有:

      • 移动介质: U盘、移动硬盘、光盘。自动播放功能曾是主要传播方式。
      • 网络下载: 从非正规网站下载的“破解软件”、“外挂程序”、“盗版电影”常常夹带病毒。
      • 电子邮件附件: 伪装成发票、订单、贺卡的附件(如 .exe, .scr, 或带宏的 .doc 文件),一旦打开就激活。
      • 网络共享与漏洞: 利用操作系统或软件的漏洞,通过网络自动传播(这类更接近蠕虫)。
      • “路过式下载”: 访问被黑客攻破的合法网站时,浏览器漏洞可能导致病毒在你不知情的情况下自动下载并安装。

      根据感染对象和破坏方式,常见病毒类型有:

      1. 文件型病毒: 感染可执行文件(.exe, .com 等)。用户运行被感染的程序时,病毒先运行,然后再悄悄执行原程序,用户难以察觉。
      2. 引导扇区病毒: 感染硬盘或U盘的引导扇区(系统启动时最先读取的地方)。现在已较少见。
      3. 宏病毒: 寄生在Office文档(如Word, Excel)的“宏”里。打开文档并启用宏时,病毒就会被激活。它可能感染其他文档,破坏文件内容。
      4. 脚本病毒: 用脚本语言(如VBScript, JavaScript)编写,通过网页或邮件传播。
      5. 多态/变形病毒高级病毒。每次复制时都会改变自身的代码形态(加密、混淆),像“变形怪”一样,使传统特征码查杀很难识别。
      6. 勒索病毒目前危害最大的一类。它感染电脑后,会加密你的所有重要文件(照片、文档、数据库),然后弹出窗口索要赎金(通常要求用比特币支付)才会提供解密密钥。

      病毒的破坏行为千奇百怪,包括但不限于:

      • 搞破坏: 删除文件、格式化硬盘、让系统崩溃蓝屏。
      • 耗资源: 大量自我复制,占用CPU和内存,导致电脑奇慢无比。
      • 恶作剧: 弹出奇怪消息、让鼠标键盘失灵、播放恐怖声音。
      • 开后门: 为黑客打开秘密通道,方便其远程控制你的电脑(变成“肉鸡”),窃取信息或发动攻击。
      • 牟利: 如勒索病毒直接敲诈;或暗中窃取网银密码、游戏账号、信用卡信息。

      五、网络安全

      1. 密码:强密码、一码一用、双重认证(密码+手机邮箱验证)
      2. 软件与设备:
        • 及时更新修补安全漏洞、官方渠道下载应用,不要点击不明链接安装。
        • 安装可靠的安全软件。
      3. 上网行为:警惕链接和附件、识别网络钓鱼,凡是索要密码、验证码、身份证号、银行卡号的,100%是诈骗。仔细看网址,诈骗网站常常用相似字母冒充真网址(如把 taobao.com 改成 taoba0.com)。
        • 使用安全链接:登录网站时确保https://且地址栏有锁型图标
        • 谨慎公开个人信息:不透露自己的住址、身份、行程、车牌等敏感信息,关闭不必要的APP权限,如:定位、通讯录、短信读取
      4. 公共WIFI
        • 避免在公共WIFI下进行敏感操作,如网银支付、登录重要账户等。防止黑客搭建同名网络窃取数据。
        • 如果必须用,用完后关闭设备的自动连接功能。
      5. 数据备份:最后的保障
        • 定期备份:奖手机、电脑里的重要文件、照片,备份到硬盘或云盘,便于找回
    17. 网站建设课

      第一阶段:HTML初识

      HTML:初识框架及常用标签

      HTML:表格表单和多媒体

      HTML:补充标签

      阶段作业:个人技术博客主页

      任务说明:请你根据近期所学,创建一个简单的个人技术博客主页。页面主题可以是你的学习笔记、前端技术分享等。页面必须使用以下内容分区标签构建整体结构。

      必须使用的结构标签

      • <header> (页眉)
      • <nav> (导航菜单)
      • <main> (主内容区)
      • <article> (博客文章)
      • <section> (章节)
      • <aside> (侧边栏)
      • <footer> (页脚)

      页面内容建议

      • 页眉:博客名称和一句简介。
      • 导航:包含“首页”、“HTML”、“CSS”、“关于我”等链接。
      • 主内容:一篇博客文章,用<article>包裹。文章内可以包含一段代码、一个技术术语解释、一个学习进度条等。
      • 侧边栏:作者简介(用<address>)、术语定义列表(用<dl>)、折叠的常见问题(用<details>)。
      • 页脚:版权声明(用&copy;)、备案信息。

      第二阶段:CSS入门

      CSS:入门及选择器

    18. 算术运算符

      运算符名称示例说明
      +加法a + b求和
      -减法a - b求差
      *乘法a * b求积
      /除法a / b求商
      %取模a % b求余数(整数)
      ++自增a++, ++a增加1
      --自减a--, --a减少1
      +一元正号+a保持符号不变
      -一元负号-a取相反数

      整数运算

      加(+)、减(-)、乘法(*)与数学一致

      • 整数除法(/):截断小数(取商)
        • cout<<10/4; // 输出2,两边都是整数,结果必然是商
        • cout<<10.0/4; // 输出2.5,两边存在浮点数,结果正常计算,默认6位有效数字
      • 整数取模(%):求余数
        • cout<<10%3; // 输出1

      浮点数运算

      加(+)、减(-)、乘法(*)、除法(/)与数学一致

      • 浮点数取模(%):无效、报错

      自增自减运算

      #include <iostream>
      using namespace std;
      
      int main() {
          int a = 5;
          
          // 前缀递增:先递增,后使用
          int b = ++a;      // a变为6,b为6
          cout << "前缀递增:" << endl;
          cout << "a = " << a << ", b = " << b << endl;  // a=6, b=6
          
          a = 5;  // 重置
          
          // 后缀递增:先使用,后递增
          int c = a++;      // c为5,a变为6
          cout << "后缀递增:" << endl;
          cout << "a = " << a << ", c = " << c << endl;  // a=6, c=5
          
          // 类似地,前缀/后缀递减
          int d = 10;
          cout << "\n前缀递减 --d: " << --d << endl;      // 9
          cout << "后缀递减 d--: " << d-- << endl;        // 9(输出后d变为8)
          cout << "d的最终值: " << d << endl;             // 8
          
          return 0;
      }
    19. 数据类型与常量变量

      数据类型

      • 整数型:shortintlonglong long
        • short占用2字节,数据范围:-32768~32767,该类型因为能存放的范围太小,平时、比赛时都不用。
        • int占用4字节,数据范围:-2147483648~2147483848,C++中整数的默认类型,平时刷题最常用,比赛时需要注意是否够用。
        • long占用4或8字节,数据范围与intlong long一致,取决于平台,不好控制,平时、比赛时都不用。
        • long long占用8字节,数据范围:-9.22*10^{18} 到 9.22*10^{18},比赛时,作为int的替代品,专注于数据范围较大的题目。(数据范围只能18位以内,数据范围超级大的题目需要用到高精度算法,后续课程才能讲到)
      • 修饰符(了解):默认signed,代表数据有正有负,如果标记成unsigned,代表没有符号位(没有负数),如unsigned short代表0~65535。
      • 浮点型:floatdouble
        • float:单精度浮点数,占用4字节,有效数位6,不够精确,基本不用
        • double:双精度浮点数,占用8字节,有效数位16,C++语言默认实数类型
      • 字符型:char
        • 占用空间1字节,数据范围:-128~127或0~255,对于字符型我们只使用0~127这个范围,因此数据范围有无符号不影响使用
      • ASCII码:使用 7 位二进制表示字符,共 128 个字符(0-127)。
        • 字符组规律
          • 字符’0’~’9’:48~57
          • 字符’A’~’Z’:65~90
          • 字符’a’~’z’:97~122
        • ASCII码表
      • 布尔型:bool
        • bool类型只有两个值:true、false,对应1和0
      • sizeof指令:用于输出某个空间占用的大小
      #include <iostream>
      using namespace std;
      int main() { 
          int a;
          cout << sizeof(a) << endl; // 4
          cout << sizeof(bool) << endl; // 1
          cout << sizeof(long long) << endl; // 8
          return 0;
      }

      类型转换

      显示转换:相当于明确告诉电脑转换成什么类型

      // 写法1:static_cast<目标类型>(当前数据)
      int a=static_cast<int>(1.5); // 将double类型的1.5转换成int类型,再赋值给a
      // 写法2:(目标类型)当前数据
      int b=(int)1.5;

      隐式转换:电脑根据自己的判断转换

      • 同类型转换:
        • 小空间->大空间无损:short->int->long long,float->double
        • 大空间->小空间丢失:long long->int->short,double->float
      • 异类型转换:
        • 整数->浮点赋值转换(同空间大小不丢失)
        • 浮点->整数赋值转换(小数部分丢失)
        • 整数->字符赋值转换(符合大空间转小空间)
        • 字符->整数赋值转换(只在ASCII码范围内)
        • 字符->整数通过数学计算转换
        • 布尔->整数赋值转换(只有0和1)
        • 整数->布尔赋值转换(只有0和1)
      int a = 123;
      long long b = a; // 赋值转换,大空间无损
      int c = 1.5; // 赋值转换,小数部分丢失
      char c = 'A';
      int d = c; // 赋值转换,变成65('A'的ASCII码值)
      bool e = 666; // 赋值转换,变成true(1)
      int f = false; // 赋值转换,变成0(false)
      char g = 'a';
      cout << g+0; // 数学计算转换,值为97('a'的ASCII码值)

      字母大小写转换

      char c = 'A';
      c += 32; // 变小写
      c -= 32; // 变大写

      常量变量

      变量:通常指可以改变的量

      常量:通常指无法改变的量

      变量使用

      int a; // 定义整型变量a,可以先不赋值
      a=6; // 将a的值修改成6
      a=7; // 将a的值修改成7
      int b=4; // 定义整型变量b并初始化为4

      常量使用

      • #define 预处理常量
      • const 定义常量
      #include <iostream> 
      using namespace std;
      #define P 3.14 // 预处理3.14用P代替
      int main(){
          // const 定义常量
          const int a=5; // 定义常量a的值为5,定义的同时必须赋值
          a=6; // 报错,常量无法修改
          return 0;
      }

    20. 计算机存储

      存储单位常识与换算

      位(bit,比特、比特位):计算机存储的最小单位。

      字节(Byte):计算机存储的基本单位,1字节=8比特。

      • 1KB = 1024B
      • 1MB = 1024KB
      • 1GB = 1024MB
      • 1TB = 1024GB

      字(通常指汉字,不常用):在不同的电脑系统中,汉字占的空间大小不完全一致,通常我们换算成:1字 = 2字节(其他还有1字 = 4字节,1字 = 8字节)。

      计算机底层存储

      计算机内存条是由许多小格子(空间)组成的,每个小格子只能存放0或1,也就是1bit的大小。

      当我们需要电脑存储数据时,需要提前申请一个空间,把原本的数据转换成二进制(只有0和1)的形式,再分别存放到小格子中。申请的空间大小,取决于我们使用的单词(数据类型),如果是int,默认申请的空间大小为4字节。

      数据类型空间大小作用
      short(短整型)2字节存放整数类型的数据
      int(整型)4字节存放整数类型的数据(默认)
      long long(长整型)8字节存放整数类型的数据
      float(单精度浮点数)4字节存放小数类型的数据
      double(双精度浮点数)8字节存放小数类型的数据(默认)
      char(字符型)1字节存放字符类型的数据
      bool(布尔型)1字节只存放真(1)、假(0)两个数据

      存储模拟过程:

      #include <iostream>
      using namespace std;
      int main(){
          int a; // L1
          cin>>a; // L2
          cout<<a; // L3
          return 0;
      }
      • L1:申请一个空间,大小4字节(32bit),名字叫做a
        • (00000000 00000000 00000000 00000000)
      • L2:输入一个数字,将其二进制存入a中,如输入6,6的二进制是110,存储后
        • (00000000 00000000 00000000 00000110)
      • L3:输出a时,电脑从内存中读取二进制,转换成数字6,输出6
    21. HTML:表格表单和多媒体

      表格:展示结构化数据

      表格用于展示数据清单,如成绩表、价格表、课程表等信息

      <table>
          <thead>
              <tr>
                  <th>标题1</th>
                  <th>标题2</th>
              </tr>
          </thead>
          <tbody>
              <tr>
                  <td>数据1</td>
                  <td>数据2</td>
              </tr>
          </tbody>
      </table>
      标签全称/含义作用特点
      <table>Table(表格)定义整个表格最外层容器,所有表格内容都写在里面
      <thead>Table Head(表格头)包裹表头行可选,但建议用,语义更清晰
      <tbody>Table Body(表格主体)包裹数据行可选,浏览器会自动补全
      <tfoot>Table Foot(表格尾)包裹汇总行可选,通常放合计、备注等
      <tr>Table Row(表格行)定义一行里面放 th 或 td
      <th>Table Header(表头单元格)定义列标题文字默认加粗居中
      <td>Table Data(表格数据)定义普通单元格文字默认常规左对齐

      常用属性

      属性作用示例适用标签
      border设置表格边框宽度border="1"<table>
      colspan横向合并单元格(跨列)colspan="2"th 或 td
      rowspan纵向合并单元格(跨行)rowspan="3"th 或 td
      align水平对齐方式align="center"tabletrthtd
      valign垂直对齐方式valign="middle"trthtd
      width设置宽度width="500px"tablethtd
      cellpadding单元格内边距(内容与边框距离)cellpadding="10"<td>
      cellspacing单元格间距(单元格之间距离)cellspacing="5"<td>

      表单:收集信息

      表单是网页交互的核心(通常用于登录、注册、搜索等功能)

      <!-- 不用 action 和 method,因为不提交 -->
      <h2>📝 同学信息登记表(仅练习布局)</h2>
      
      <!-- 用 div 或 表格 把表单项排整齐,不用 form 也可以 -->
      <div>
          <!-- 文本框 -->
          <label>姓名:</label>
          <input type="text" placeholder="例如:张三">
      </div>
      
      <div>
          <label>年龄:</label>
          <input type="number" placeholder="请输入数字">
      </div>
      
      <div>
          <label>性别:</label>
          <input type="radio" name="gender"> 男
          <input type="radio" name="gender"> 女
      </div>
      
      <div>
          <label>爱好:</label>
          <input type="checkbox"> 阅读
          <input type="checkbox"> 运动
          <input type="checkbox"> 音乐
      </div>
      
      <div>
          <label>城市:</label>
          <select>
              <option>北京</option>
              <option>上海</option>
              <option>广州</option>
          </select>
      </div>
      
      <div>
          <label>自我介绍:</label>
          <textarea rows="3" cols="30" placeholder="简单说几句..."></textarea>
      </div>
      
      <!-- 按钮可以点,但不会真提交 -->
      <button>模拟提交(无后端)</button>
      <button type="button">清空(示例按钮)</button>
      
      <p style="color: gray;">※ 当前仅为界面练习,不保存数据</p>
      标签全称/含义作用特点
      <form>Form(表单)定义整个表单区域所有表单项的容器,目前只用来“框住”表单项
      <input>Input(输入)定义输入控件通过 type 属性变化出各种形态(文本框、按钮、复选框等)
      <label>Label(标签)为输入控件定义说明文字点击文字也能聚焦到对应输入框,提升体验
      <select>Select(选择)定义下拉菜单需要配合 <option> 使用
      <option>Option(选项)定义下拉菜单中的一个选项包裹在 <select> 里面
      <textarea>Textarea(文本区域)定义多行文本输入框可以调整宽高,适合输入较长的内容
      <button>Button(按钮)定义可点击的按钮比 <input> 按钮更灵活,内部可以放文字、图标等

      <input>常用属性

      属性作用示例适用 type
      name控件名称(类似变量的名字)name="username"所有 type(除了按钮类)
      value控件的值(实际内容)value="张三"所有 type
      placeholder占位提示文字(浅灰色)placeholder="请输入姓名"textemailpassword 等文本类
      required必填项(不填无法提交)required所有输入类
      disabled禁用控件(灰色不可编辑)disabled所有 type
      readonly只读(不能修改)readonlytexttextareanumber 等
      checked默认选中checkedradiocheckbox
      selected下拉菜单默认选中(用在 <option>selected<option>
      maxlength最大字符数maxlength="10"文本类
      min / max最小值 / 最大值min="0" max="100"numberrangedate
      step步长(每次增减多少)step="5"numberrange
      multiple多选(文件、下拉菜单)multiplefileselect

      <select>和<option>相关

      <!-- 基础下拉菜单 -->
      <select name="city">
          <option value="beijing">北京</option>
          <option value="shanghai">上海</option>
          <option value="guangzhou">广州</option>
      </select>
      
      <!-- 带默认选中的选项 -->
      <select name="city">
          <option>北京</option>
          <option selected>上海</option>  <!-- selected让上海默认显示 -->
          <option>广州</option>
      </select>
      
      <!-- 分组的下拉菜单 -->
      <select name="fruit">
          <optgroup label="国产水果">
              <option>苹果</option>
              <option>香蕉</option>
          </optgroup>
          <optgroup label="进口水果">
              <option>车厘子</option>
              <option>牛油果</option>
          </optgroup>
      </select>
      标签/属性作用
      <select>下拉菜单容器
      <option>单个选项
      <optgroup>选项分组
      selected默认选中某个选项
      multiple多选(按住 Ctrl 可多选)

      <textarea>相关

      <!-- 基础文本域 -->
      <textarea rows="4" cols="50" placeholder="请输入自我介绍..."></textarea>
      
      <!-- 禁止调整大小(通常用CSS,这里仅作了解) -->
      <textarea style="resize: none;" rows="3" cols="30"></textarea>
      属性作用示例
      rows显示的行数(高度)rows="4"
      cols显示的列数(宽度)cols="50"
      placeholder占位提示文字placeholder="请输入..."
      maxlength最大字符数maxlength="200"
      wrap自动换行方式wrap="soft" 或 wrap="hard"

      <button>相关

      <!-- 按钮内部可以放任何内容(文字、图标等) -->
      <button type="button">
          <strong>👍</strong> 点赞
      </button>
      type 值作用示例
      submit提交按钮(默认值)<button type="submit">提交</button>
      reset重置按钮(清空表单内容)<button type="reset">清空</button>
      button普通按钮(无默认行为)<button type="button">点我</button>

      <label>相关

      用法代码效果
      用 for 绑定 id<label for="username">用户名:</label>
      <input type="text" id="username">
      点击“用户名”文字,光标自动跳入输入框
      直接包裹<label>用户名:<input type="text"></label>同上,但不需要 for 和 id

      多媒体:音频与视频

      <!-- 视频 -->
      <video width="600" controls>
          <source src="movie.mp4" type="video/mp4">
          您的浏览器不支持 video 标签。
      </video>
      
      <!-- 音频 -->
      <audio controls>
          <source src="music.mp3" type="audio/mpeg">
          您的浏览器不支持 audio 标签。
      </audio>

      相关标签

      标签全称/含义作用特点
      <img>Image(图像)在网页中嵌入图片单标签,不需要结束符
      <video>Video(视频)在网页中嵌入视频双标签,内部可放多个 <source>
      <audio>Audio(音频)在网页中嵌入音频双标签,内部可放多个 <source>
      <source>Source(资源源)为视频/音频提供多个备用格式嵌套在 <video> 或 <audio> 内部
      <track>Track(轨道)为视频添加字幕嵌套在 <video> 内部,可选

      <img>相关

      <img src="图片地址" alt="图片描述">
      属性作用示例说明
      src图片路径(必须)src="photo.jpg"可以是网络地址或本地路径
      alt替代文本(必须)alt="一只小猫"图片加载失败时显示,对盲人读屏软件友好,也有利于SEO
      width宽度width="300"单位是像素(px),可省略写数字
      height高度height="200"单位是像素(px),建议只设置一个,另一个自动等比缩放
      title鼠标悬停提示文字title="点击查看大图"鼠标放上去会显示小浮窗
      loading加载方式loading="lazy"lazy 懒加载(滚动到才加载),eager 立即加载

      <video>相关

      <video src="movie.mp4" controls></video>
      
      <!-- 支持多个备用格式 -->
      <video controls>
          <source src="movie.mp4" type="video/mp4">
          <source src="movie.webm" type="video/webm">
          您的浏览器不支持 video 标签。
      </video>
      属性作用示例说明
      src视频路径src="video.mp4"单视频时可写在video上,多视频时用source
      controls显示播放控件controls布尔属性(写了就生效),显示播放/暂停/音量等按钮
      autoplay自动播放autoplay页面加载后自动播放(⚠️ 多数浏览器限制需静音)
      muted静音muted静音播放,常配合 autoplay 使用
      loop循环播放loop播放完后自动重播
      poster封面图poster="cover.jpg"视频加载前或未播放时显示的图片
      width宽度width="640"视频宽度(像素)
      height高度height="360"视频高度(像素)
      preload预加载preload="auto"none(不预加载)/ metadata(只加载元数据)/ auto(预加载全部)
      playsinline内联播放playsinline在移动端禁止全屏播放,就在页面内播放

      <audio>相关

      <audio src="music.mp3" controls></audio>
      
      <!-- 支持多个备用格式 -->
      <audio controls>
          <source src="music.mp3" type="audio/mpeg">
          <source src="music.ogg" type="audio/ogg">
          您的浏览器不支持 audio 标签。
      </audio>
      属性作用示例说明
      src音频路径src="music.mp3"单音频时可写在audio上
      controls显示播放控件controls显示播放/暂停/音量/进度条
      autoplay自动播放autoplay自动播放(⚠️ 多数浏览器限制)
      muted静音muted静音播放
      loop循环播放loop播放完后重播
      preload预加载preload="auto"none / metadata / auto

      <source>相关

      属性作用示例说明
      src媒体文件路径src="video.mp4"必须
      type文件MIME类型type="video/mp4"帮助浏览器快速判断能否播放

      <track>相关

      <video controls>
          <source src="movie.mp4" type="video/mp4">
          <track src="subtitles_en.vtt" kind="subtitles" srclang="en" label="English">
          <track src="subtitles_zh.vtt" kind="subtitles" srclang="zh" label="中文">
      </video>
      属性作用示例
      src字幕文件路径(WebVTT格式,.vtt文件)src="subtitle.vtt"
      kind轨道类型subtitles(字幕)/ captions(隐藏字幕)/ descriptions(描述)/ chapters(章节)
      srclang语言代码srclang="zh" / srclang="en"
      label显示的标题label="中文"

      文件格式要求

      • 图片:jpeg、jpg、png、gif、webp、svg等
      • 视频:mp4、webm、ogg等
      • 音频:mp3、wav、ogg

    22. 基本数据类型与输入输出

      数据类型初步

      分类:

      • 整数型:intlong long
      • 浮点型:floatdouble
      • 字符型:char
      • 布尔型:bool

      创建变量:

      int a; // 创建一个能够存放整数的空间,空间名字叫做 a
      int b,c; // 连续创建两个能够存放整数的空间,空间名字分别叫做 b 和 c
      float d; // 创建一个能够存放浮点数的空间,空间名字叫做 d

      规则:

      变量名(标识符)只能由字母、数字、下横线组成,数字不能开头,有意义的关键字不能作为标识符使用,如int,using

      输入指令:cin

      基本语法

      cin >> 变量名;

      使用流程

      int x; // 先申请空间,命名
      cin >> x; // 后输入数据,存放

      连续输入多个值

      int a, b, c;
      cin >> a >> b >> c;            // 用空格或回车分隔输入
      // 输入:10 20 30  → a=10, b=20, c=30

      输出指令:cout

      基本语法

      cout << "要输出的内容";

      输出文字(字符串)

      cout << "Hello World";      // 双引号包裹文字
      cout << "你好,世界";        // 支持中文

      输出数字

      cout << 123;                // 直接写数字
      cout << 3.14;               // 小数

      换行方法

      cout << "第一行" << endl;   // 方法1:endl
      cout << "第二行\n";         // 方法2:\n(反斜杠n)

      连续输出多个内容

      cout << "姓名:" << "小明" << ",年龄:" << 10;
      // 输出:姓名:小明,年龄:10

      重要规则

      1. 符号全英文:; " << 必须是英文符号
      2. 每句结尾加分号:cout语句必须以;结束
      3. 箭头方向:<< 把内容输出到屏幕
      4. 文字加引号:文字必须用” “包裹

      常用转义字符

      cout << "换行:第一行\n第二行";     // \n = 换行 相当于换行符
      cout << "引号:他说:\"你好\"";      // \" = 输出双引号
      cout << "路径:C:\\Program";        // \\ = 输出反斜杠
      cout << "制表符:姓名\t年龄";        // \t = 空格对齐 相当于制表符

      进阶用法:格式化输出<iomanip>

      控制小数位数

      #include <iomanip>  // 需要这个头文件
      #include <iostream>
      using namespace std;
      
      int main()
      {
          double pi = 3.1415926;
          cout << "默认:" << pi << endl;                // 3.14159
          cout << "保留2位:" << fixed << setprecision(2) << pi << endl;  // 3.14
          cout << "保留5位:" << setprecision(5) << pi << endl;           // 3.14159
          return 0;
      }

      控制宽度对齐

      #include <iomanip>
      #include <iostream>
      using namespace std;
      
      int main()
      {
          // 设置宽度为10,右对齐(默认)
          cout << setw(10) << "姓名" << setw(10) << "成绩" << endl;
          cout << setw(10) << "小明" << setw(10) << 95 << endl;
          cout << setw(10) << "小红" << setw(10) << 88 << endl;
          
          // 左对齐
          cout << left;
          cout << setw(10) << "姓名" << setw(10) << "成绩" << endl;
          cout << setw(10) << "小明" << setw(10) << 95 << endl;
          
          return 0;
      }

      填充字符

      cout << setfill('*') << setw(20) << "欢迎" << endl;
      // 输出:****************欢迎

      进制输出

      int num = 255;
      cout << "十进制:" << num << endl;            // 255
      cout << "十六进制:" << hex << num << endl;   // ff
      cout << "八进制:" << oct << num << endl;     // 377
      cout << "切回十进制:" << dec << num << endl; // 255

      显示前缀

      cout << showbase;  // 显示进制前缀
      cout << hex << 255 << endl;  // 0xff
      cout << oct << 255 << endl;  // 0377
      cout << noshowbase;  // 关闭前缀显示
    23. 计算机语言常识

      描述:计算机是人与计算机沟通的桥梁,用于编写指令控制计算机完成特定任务。

      语言分类

      层次分类

      1. 机器语言
        • 二进制代码,只有0和1
        • 计算机能够直接执行
        • 底层操作
      2. 汇编语言
        • 用助记符代替二进制
        • 需要汇编器转化成二进制,如x86汇编、ARM汇编
        • 用于嵌入式系统,驱动程序等
      3. 高级语言
        • 接近人类的语言
        • 需要编译器或解释器进行转译执行,如:Python、Java、C++等
        • 用于软件开发,数据分析等

      执行分类

      1. 编译型语言:
        • 代码整体转换成机器码后再执行(如:C、C++、Go、Swift)
        • 流程:源代码->编译器->机器码/字节码->执行
        • 优点:执行速度快
        • 缺点:跨平台性差(不能跨系统)
      2. 解释型语言:
        • 逐行解释执行(如:Python、PHP、Lua)
        • 流程:源代码->解释器->执行
        • 优点:跨平台性强
        • 缺点:速度较慢
      3. 混合型语言(非重点):先编译成中间码,再解释执行(Java、C#)

      思想分类

      1. 面向对象:以“对象”为中心,封装、继承、多态
        • 如:Java、C++、Python、C#、PHP、JavaScript、Swift
      2. 面向过程:以“步骤”为中心,强调函数调用
        • 如:C语言,Fortran、Pascal、Ada、Go

    24. 计算机历史与人物

      发展历程

      机械计算时代

      • 帕斯卡计算器(1642年)
        • 发明者:法国数学家 步莱兹·帕斯卡
        • 特点:纯机械,只能加减法
        • 意义:第一台机械计算器
      • 差分机(1822年)
        • 发明者:英国 查尔斯·巴贝奇
        • 目标:计算数学表格
        • 故事:设计过于超前,当时技术不足,没做出来
        • 后建:1991年伦敦科学博物馆打造成功,并且真的能工作
      • 分析机(1837年)
        • 发明者:英国 查尔斯·巴贝奇
        • 设计理论:输入打孔卡片->处理数据->存储数据->输出打印
        • 地位:现代计算机的理论设想,没造出来
      • 世界第一位程序员:阿达·洛夫莱斯
        • 贡献:为分析机写算法
        • 荣誉:第一个计算机程序员
        • 编程语言Ada:以此人命名

      计算机时代

      • 第一代计算机(1946年-1958年)
        • 元件:电子管
        • 存储:水银延迟线磁鼓
        • 大小:堪比房屋
        • 速度:较慢(每秒计算几千次)
        • 耗电:村镇级耗电量
        • 缺陷:电子管容易过载烧坏
        • 代表机器:
          • 世界第一台计算机:ENIAC,编程靠插拔电线,用于计算炮弹轨迹
          • 第一台存储程序的计算机:EDSAC,采用了冯诺伊曼架构
          • 第一台商用计算机:UNIVAC I,价值100万美元(现在大概7000万人民币)
          • 中国第一台计算机:103机(1958年),每秒计算30次,象征着中国计算机事业的起步
      • 第二代计算机(1959年-1964年)
        • 发明者:贝尔实验室(获得诺贝尔奖)
        • 元件:晶体管代替电子管
        • 有点:比电子管小、快、省电、耐用
        • 大小:堪比衣柜
        • 速度:一般(每秒计算几百万次)
        • 代表机器:
          • IBM 1401(1959年):采用磁芯存储器
          • CDC 6600(1964年):采用氟利昂降温(当时世界最快计算机)
      • 第三代计算机(1965年-1970年)
        • 集成电路计算机
        • 原理:把多个晶体管做在一个芯片上
        • 大小:桌子大小
        • 普及:大学、公司开始使用
        • 出现操作系统和文件系统
        • 高级语言:FORTRAN、COBOL
      • 第四代计算机(1971年-现在)
        • 超大规模集成电路,微处理器
        • 普及:个人用户可以使用
      • 未来:量子计算机、生物计算机、神经形态、人工智能

      名人事迹

      • 阿兰·图灵 : 计算机科学理论之父,人工智能之父,图灵测试,图灵奖
      • 冯·诺依曼 : 存储程序架构,电子计算机之父
      • 比尔·盖茨 : 微软与PC操作系统
      • 史蒂夫·乔布斯 : 苹果与用户体验革命
      • 蒂姆·伯纳斯-李 : 发明万维网
      • 姚期智 : 中国唯一图灵奖获得者,清华姚班、智班
      • 王选 : 汉字激光照排系统,“王选奖”
      • 克劳德·香农 : 信息论之父,信息熵,提出以bit作为基本单位
      • 查尔斯·巴贝奇 : 现代计算机鼻祖,差分机,解析机
      • 艾达·罗夫莱斯 : 世界第一个程序员
      • 戈登·摩尔 : 英特尔创始人之一,摩尔定律
    25. C++基本结构与cout入门

      程序基本框架

      #include <iostream>      // 预处理操作:导入输入输出头文件
      using namespace std;     // 使用std命名空间
      
      int main()               // 主函数(固定,必须有)
      {                        // { 主函数的开始区域标记
          // 你的代码写在这里   	 
          return 0;            // 结束程序指令
      }                        // } 主函数的结束区域标记
      1. 头文件:提前告诉电脑我的程序需要的工具
      2. 命名空间:使用标准工具,简写代码
      3. 主函数:程序的起点、必须这样写,不要忘记main后面的()
      4. 大括号:主函数的使用范围
      5. 结束语句:告诉电脑,程序可以结束了

      cout用法

      cout << 具体内容;

      这里的具体内容可以是””(双引号)括起来的任意内容,可以是简单的数学表达式,可以是有意义的程序指令,如下:

      cout << "hello fangt.online"; // 输出:hello fangt.online
      cout << 1*2-3*4; // 输出:-10
      cout << endl; // 换行指令

      更多用法

      连续输出

      cout << "hello" << endl << 1*2 << 65 << endl << "4*5";

      输出特定符号:\n 换行符号、\t 制表符号

      cout << "hello\nworld";
      cout << 123 << "\t" << 34322 << "\n";
      cout << 122343 << "\t" << 322 << "\n";
      cout << 1 << "\t" << 2 << "\n";
      cout << 12223 << "\t" << 34322 << "\n";

      程序实例练习

      最简单的程序

      #include <iostream>
      using namespace std;
      
      int main()
      {
          cout << "我的第一个C++程序!";
          return 0;
      }

      输出个人信息

      #include <iostream>
      using namespace std;
      
      int main()
      {
          cout << "================" << endl;
          cout << "   个人信息卡    " << endl;
          cout << "================" << endl;
          cout << "姓名:张小华" << endl;
          cout << "年龄:10岁" << endl;
          cout << "学校:星星小学" << endl;
          cout << "爱好:编程、足球" << endl;
          cout << "================" << endl;
          return 0;
      }

      输出图案

      #include <iostream>
      using namespace std;
      
      int main()
      {
          cout << "  *  " << endl;
          cout << " *** " << endl;
          cout << "*****" << endl;
          cout << " *** " << endl;
          cout << "  *  " << endl;
          return 0;
      }

      易错点

      1. 英文符号:所有符号都用英文(; “” <>)
      2. 大小写敏感coutCoutmainMain
      3. 分号结束:每句指令以;结束
      4. 引号包裹:文字要用" "包起来
      5. 框架完整:必须有头文件、main函数、return 0

      格式建议

      // ✅ 好格式(清晰易读)
      #include <iostream>
      using namespace std;
      
      int main()
      {
          cout << "你好";
          return 0;
      }
      
      // ❌ 差格式(太挤)
      #include <iostream>
      using namespace std;
      int main(){cout<<"你好";return 0;}

      拓展内容

      注释

      // 这是单行注释
      cout << "你好";  // 注释也可以跟在代码后面
      
      /* 
         这是多行注释
         可以写很多行
      */
    26. 冯诺伊曼体系结构

      人物介绍

      • 名字:约翰·冯·诺伊曼(John von Neumann)
      • 年代:1903-1957年
      • 国籍:匈牙利裔美国人
      • 称号:”计算机之父”、”现代计算机架构之父”
      • 历史
        • 6岁:能用古希腊语讲笑话
        • 8岁:掌握微积分
        • 30岁:成为普林斯顿大学教授
        • 40岁:提出改变世界的计算机设计

      冯诺伊曼结构

      核心定义

      • 冯·诺伊曼结构 = 计算机的”标准设计图纸”
      • 规定了计算机必须有的五大部件以及它们如何合作工作
      • 五大部件:存储器、运算器、控制器、输入设备、输出设备
      • 思想:把计算过程描述为由许多指令按一定顺序组成的程序,然后把程序和数据输入计算机,计算机对已经存入的程序和数据进行处理后,输出结果

      特点

      1. 存储程序(革命性意义,换个房子变成换个菜谱)
      2. 二进制存储(简单可靠、抗干扰)
      3. 顺序执行(指令一行一行的按顺序执行)

      其他相关

      1. ENIAC:第一台电子计算机(不是冯诺依曼结构)
      2. EDVAC:第一台存储程序计算机
      3. 哈佛结构:另一种设计(程序和数据分开存)

      名人名言:

      “如果人们不相信数学简单,只是因为他们没有意识到生活有多复杂。” ——冯·诺伊曼

    27. 计算机软硬件组成

      计算机类似一个机器人,由身体(硬件)及思维(软件)组成。

      硬件

      • 输入设备:键盘、鼠标、触摸屏、麦克风、摄像头、扫描仪
      • 输出设备:显示器、打印机、音响(耳机)、投影仪
      • 中央处理器(CPU):计算机的大脑,由控制器、运算器、寄存器组成
      • 存储器:RAM、ROM、U盘等
      • 主板:计算机的骨架,包含大量配件接口
      • 其他:总线结构、电源等

      软件

      • 系统软件:
        • 操作系统:电脑(Windows、MacOS、Linux),手机(IOS、Android、鸿蒙)
        • 驱动程序:打印机驱动、显卡驱动、摄像头驱动
      • 应用软件:
        • 办公软件:Word、PPT
        • 工具软件:浏览器、DEV、杀毒软件
        • 娱乐软件:音乐播放器、游戏
    28. [GESP2306三级] 春游

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,m,x,a[1005]={};
          cin>>n>>m;
          while(m--){
              cin>>x;
              a[x]++;
          }
          int cnt=0;
          for(int i=0;i<n;i++){
              if(a[i]) cnt++;
          }
          if(n==cnt) cout<<n;
          else{
              for(int i=0;i<n;i++){
                  if(!a[i]) cout<<i<<" ";
              }
          }
          return 0;
      }
    29. [GESP2309三级] 小杨的储蓄

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,d,a[1005]={},g[1005]={};
          cin>>n>>d;
          for(int i=1;i<=d;i++){
              cin>>a[i];
              g[a[i]]+=i;
          }
          for(int i=0;i<n;i++){
              cout<<g[i]<<" ";
          }
          return 0;
      }
    30. [GESP2403三级] 完全平方数

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,a[1005]={};
          cin>>n;
          for(int i=1;i<=n;i++){
              cin>>a[i];
          }
          int cnt=0;
          for(int i=1;i<=n;i++){
              for(int j=i+1;j<=n;j++){
                  int x=a[i]+a[j];
                  int y=sqrt(x);
                  if(y*y==x) cnt++;
              }
          }
          cout<<cnt;
          return 0;
      }
    31. [GESP2406三级] 寻找倍数

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int t,n;
          cin>>t;
          while(t--){
              bool f=true;
              int a[100005]={};
              cin>>n;
              int maxa=-1;
              for(int i=1;i<=n;i++){
                  scanf("%d",&a[i]);
                  maxa=max(maxa,a[i]);
              }
              for(int i=1;i<=n;i++){
                  if(maxa%a[i]!=0){
                      f=false;
                  }
              }
              if(f) cout<<"Yes\n";
              else cout<<"No\n";
          }
          return 0;
      }
    32. [GESP2503三级] 2025

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int x;
          cin>>x;
          for(int i=1;i<=2025;i++){
              if((x&i)+(x|i)==2025){
                  cout<<i;
                  break;
              }
          }
          return 0;
      }
    33. 1126:矩阵转置

      #include <iostream>
      using namespace std;
      int main(){
      	int n,m,a[150][150]={};
      	cin>>n>>m;
      	for(int i=0;i<n;i++){
      		for(int j=0;j<m;j++){
      			cin>>a[i][j];
      		}
      	}
      	for(int i=0;i<m;i++){
      		for(int j=0;j<n;j++){
      			cout<<a[j][i]<<" ";
      		}
      		cout<<endl;
      	}
      	return 0;
      }
    34. 1121:计算矩阵边缘元素之和

      #include <bits/stdc++.h>
      
      using namespace std;
      
      int main()
      {
          int n,m;
          cin>>n>>m;
          int a[100][100]={};
          for(int x=1;x<=n;x++){
              for(int y=1;y<=m;y++){
                  cin>>a[x][y];
              }
          }
          int s=0;
          for(int x=1;x<=n;x++){
              for(int y=1;y<=m;y++){
                  if(x==1||x==n||y==1||y==m) s+=a[x][y];
              }
          }
          cout<<s;
          return 0;
      }
    35. 1119:矩阵交换行

      #include <bits/stdc++.h>
      
      using namespace std;
      
      int main()
      {
          int a[5][5];
          for(int i=0;i<5;i++){
              for(int j=0;j<5;j++){
                  cin>>a[i][j];
              }
          }
          int m,n;
          cin>>m>>n;
          int ind[5]={0,1,2,3,4};
          swap(ind[m-1],ind[n-1]);
          for(int i=0;i<5;i++){
              for(int j=0;j<5;j++){
                  cout<<a[ind[i]][j]<<" ";
              }
              cout<<endl;
          }
          return 0;
      }
    36. 1124:矩阵加法

      #include<iostream>
      using namespace std;
      int main ()
      {
          int n,m;
          cin>>n>>m;
          int a[100][100]={};
          int b[100][100]={};
          for (int i=0;i<n;i++)
          {
              for(int j=0;j<m;j++)
              {
                  cin>>a[i][j];
              }
          }
          for (int o=0;o<n;o++)
          {
              for(int c=0;c<m;c++)
              {
                  cin>>b[o][c];
              }
          }
          for(int k=0;k<n;k++)
          {
          	for(int s=0;s<m;s++)
          	{
          	    a[k][s]=a[k][s]+b[k][s];
      	}
          }
          for (int l=0;l<n;l++)
          {
              for(int p=0;p<m;p++)
              {
                  cout<<a[l][p]<<" ";
              }
              cout<<endl;
          }
          return 0;
      }
    37. 2041:【例5.9】新矩阵

      #include <bits/stdc++.h>
      using namespace std;
      int main()
      {
          int n;
          cin>>n;
          int a[25][25]={};
          for(int i=0;i<n;i++){
              for(int j=0;j<n;j++){
                  cin>>a[i][j];
              }
          }
          for(int i=0;i<n;i++){
              for(int j=0;j<n;j++){
                  if(i==j||i+j==n-1) a[i][j]+=10;
                  cout<<a[i][j]<<" ";
              }
              cout<<endl;
          }
          return 0;
      }
    38. 字符数组基本用法

      字符数组就是字符类型的数组,与整数型数组的用法完全相同

      #include <iostream>
      using namespace std;
      
      int main()
      {
      	// 字符数组初始化
      	char c[5];
      	char d[5]={'a','b','c','d','e'};
      	char e[]={'a','b','c','d','e'}; // 自动计算长度
      	
      	
      	// 字符数组基本输入输出
      	char f[1000]={};
      	int n;
      	cin>>n;
      	for(int i=0;i<n;i++){
      		cin>>f[i];
      	}
      	for(int i=0;i<n;i++){
      		cout<<f[i]<<" ";
      	}
      	
          return 0;
      }

      字符数组独有的用法

      #include <iostream>
      using namespace std;
      
      int main()
      {
      	// 字符数组初始化
      	char c[6] = {'h','e','l','l','o','\0'}; // '\0'作为结束标记,占位确不算长度
      	
      	char d[] = "hello"; // 将每个字符一一存放进数组中(包含'\0'),自动计算长度
      	
      	// 字符数组高级输入输出
      	char e[10];
      	cin>>e; // 整行输入,一一存放,遇到空格或换行结束
      	cout<<e; // 输出整行,遇到'\0'结束
      	
              cin.ignore();
      	cin.getline(e,10); // 整行输入,包括空格
      	
          return 0;
      }

      字符数组相关的功能函数

      strlen (arr) —— 求字符串长度(不含 \0)

      char c[] = "abcde";
      int len = strlen(c);
      cout<<len; // 5

      strcmp (arr1, arr2) —— 比较两个字符串ascii码

      char c1[]="abc";
      char c2[]="123";
      int res = strcmp(c1,c2);
      cout<<res; // 正数c1大,负数c2大,0一样大

      strcat (arr1, arr2) —— 字符串拼接

      char c1[50]="abc";
      char c2[]="123";
      strcat(c1,c2); // 拼接时,c1需要有足够的空间
      cout<<c1; // "abc123"
    39. 字符串基本操作

      什么是字符串?

      字符串就是一串字符,像项链一样把一个个字符串起来。

      在C++中,字符串的数据需要用“”(双引号)包起来,这里123与”123″是不相等的

      创建一个字符串变量,并初始化为”hello”

      string s = "hello"; 

      输出字符串

      string s = "hello"
      cout << s; // "hello" 输出时不显示双引号

      输入字符串(无空格)

      string s;
      cin >> s; // 如果输入的字符串不包含空格,可以使用cin输入

      输入字符串(有空格)

      string s;
      getline(cin,s); // 如果输入的字符串可能有空格,必须使用getline正行读取

      注意:cin与getline混用时需要用cin.ignore()处理垃圾数据

      按字符输出(字符串可以当成数组来用,可以使用下标)

      string s = "hello";
      for(int i=0;i<s.size();i++){
          cout<<s[i]<<" ";
      }
    40. 字符串常用功能

      字符串初始化操作

      常见操作

      string s1; // s1 = ""
      string s2 = string(); // s2 = ""
      string s3 = "Hello"; // 拷贝初始化为"hello"

      构造初始化

      string s1("World"); // 直接初始化
      string s2(5, 'A'); // "AAAAA"
      string s3(10, '*');  // "**********"         

      列表初始化

      string s1{'R'}; // "R"
      string s2{'a','b','c','\0'}; // "abc"
      string s3{"C++"}; // "C++"

      字符串访问操作

      下标访问 []

      string s = "hello";
      char c = s[1]; // 'e'
      char c2 = s[10]; // 越界不抛异常,但有风险崩溃

      安全访问 at()

      string s = "hello";
      char c = s.at(1); // 'e'
      char c2 = s.at(10); // 抛异常

      首尾访问 front() back()

      string s = "hello";
      char c = s.front(); // 'h'
      char c2 = s.back(); // 'o'

      迭代器访问 begin() end()

      string s = "hello";
      auto it1 = s.begin();
      cout<<*it1; // 'h'

      容量 empty() size() length()

      string s = "hello";
      bool f = s.empty(); // 空true 非空false
      int a = s.size(); // 5 大小
      int b = s.length(); // 5 长度,与size作用相同

      字符串数据操作

      字符串清空 clear()

      string s = "hello";
      s.clear();
      cout<<s.size(); // 0,字符串中什么都没有了,大小为0

      字符串更改大小 resize()

      string s = "hello";
      s.resize(3); // "hel" 截断
      s.resize(8); // "hel"+5个'\0'
      cout<<s.size(); // 8
      s.resize(10,'k'); // "helkkkkkkk" 空字符填充'k'

      字符串插入 insert()

      string s = "hello";
      s.insert(2,"xyz"); // "hexyzllo" 2下标位置插入xyz

      字符串增加、删除末尾字符 push_back() pop_back()

      string s = "hello";
      s.push_back('A'); // "helloA"
      
      string s = "hello";
      s.pop_back(); // "hell"

      字符串追加多个内容 append() +=

      string s = "hello";
      s.append("world"); // "helloworld"
      s.append(5,"!"); // "helloworld!!!!!"
      
      s+="~~~"; // "helloworld!!!!!~~~"

      字符串删除字符 erase()

      string s = "helloworld";
      s.erase(5,3); // "hellold"从5下标删除3个字符

      字符串替换字符 replace()

      string s = "hello world";
      s.replace(6,5,"C++"); // "hello C++"从6下标开始的5个字符替换成C++

      字符串提取字串 substr()

      string s = "hello world";
      string str1 = s.substr(7); // "orld" 从下标7开始到最后
      string str2 = s.substr(4,3); // "o w" 从下标4开始的3个字符

      字符串倒序 reverse() 非字符串独有用法

      string s = "12345";
      reverse(s.begin(),s.end());
      cout<<s; // "54321"

      字符串比较 compare()

      非复杂要求的情况下建议直接使用关系运算符

      字符串的类型转换

      字符串转数值

      string s1 = "123";
      int a = stoi(s1); // 将s1转换成数字123并赋值给a
      
      string s2 = "12345678912345";
      long long b = stoll(s2); // 将s2转换成数字12345678912345并赋值给b
      
      string s3 = "123.456";
      float c = stof(s3); // 将s3转换成数字123.456并赋值给c
      
      string s4 = "-123.456";
      double d = stod(s4); // 将s4转换成数字-123.456并赋值给d

      高级用法自行了解(锁定标记、自动转进制)

      数值转字符串

      int num = 12345;
      string s1 = to_string(num); // 将整数转换成"12345"并赋值给s1
      
      double pi = 3.14;
      string s2 = to_string(pi); // 将浮点数转换成"3.140000"并赋值给s2(默认精度6位)

      字符串转字符数组

      string s = "hello";
      const char* c = s.c_str();

      补充:字符串查询find()

      如果存在,则返回下标位置,不存在,则返回unsigned long long的最大值,如果结果赋值给int类型,则是-1

      string s = "hello";
      int a = s.find("ll"); // 查询字符串s中是否包含子串"ll"
      int b = s.find('e'); // 查询字符串s中是否包含字符'e'

    41. 信奥基础课:语法进阶

      课程笔记01

      最大公约数

      算法描述

      GESP二级真题合集(一)

      课程笔记02

      一维数组

      GESP二级真题合集(二)

      2035110211101112
      1117

      课程笔记03

      进制运算初步

      位运算初步

      入门算法:枚举、模拟

      GESP二级真题合集(三)

      2036110720382037
      1104110611091116

      课程笔记04

      进制运算补充

      原码、反码、补码

      排序算法

      冒泡排序(Bubble Sort)

      GESP二级真题合集(四)

      20391310
      1118111511141113

      课程笔记05

      计数排序(Counting Sort)

      二维数组

      GESP三级真题合集(一)

      20411124
      111911211126

      课程笔记06

      字符数组基本用法

      字符串基本操作

      字符串常用功能

      2046:【例5.15】替换字母2047:【例5.16】过滤空格2048:【例5.18】串排序2049:【例5.19】字符串判等2050:【例5.20】字串包含
      1130:找第一个只出现一次的字符1131:基因相关性1132:石头剪子布1139:整理药名1143:最长最短单词

      课程笔记07

      阶段性总结与提高

      自定义函数入门

      GESP三级真题合集(二)

      1154:亲和数1155:回文三位数1152:最大数max(x,y,z)1151:素数个数

      课程笔记08

      自定义函数深入

      常用自定义函数总结

      GESP三级真题合集(三)

      1403140514001398

      课程笔记09

      递归函数

      GESP三级真题合集(四)

      课程笔记10

      选择排序

      插入排序

      随机数功能

      素数筛:埃氏筛

      课程笔记11

      GESP二级相关技巧

      GESP三级相关技巧

      课程笔记12

      阶段总结

    42. 信奥基础课:语法启蒙

      课程笔记01

      计算机软硬件组成

      冯诺伊曼体系结构

      C++基本结构与cout入门

      2060:【例1.1】计算机输出2061:【例1.2】梯形面积2063:【例1.4】牛吃牧草1001:Hello,World!

      课程笔记02

      计算机历史与人物

      计算机语言常识

      基本数据类型与输入输出

      2062:【例1.3】电影票1002:输出第二个整数1003:对齐输出1007:计算(a+b)×c的值

      课程笔记03

      计算机存储

      数据类型与常量变量

      算术运算符

      1009:带余除法1011:甲流疫情死亡率1025:保留12位小数的浮点数
      1017:浮点型数据类型存储空间大小1028:字符菱形1010:计算分数的浮点数值1013:温度表达转化
      1014:与圆相关的计算1016:整型数据类型存储空间大小1020:打印ASCII码2069:【例2.12 】糖果游戏

      课程笔记04

      计算机网络

      逻辑运算符与分支结构

      1040205110412054
      2055103910422053

      课程笔记05

      关系运算符

      GESP一级真题合集(一)

      前三章所有题目完成

      课程笔记06

      循环结构

      201720161059
      1060201820191061
      106210631064

      课程笔记07

      其他程序结构

      202120202022
      1085108710741075
      107210861076

      课程笔记08

      启蒙阶段常用算法技巧

      107110881090
      2031108010781089
      10951094

      课程笔记09

      启蒙阶段常用算法技巧(续)

      202720262029
      1097203310982032
      1099

      课程笔记10

      格式化输入输出

      GESP一级真题合集(二)

      课程笔记11

      常用自带工具(函数)

      207310371038
      1068107710571058

      课程笔记12

      GESP一级客观题总结

      第四章所有题目完成


      其他相关常识

    43. 信奥基础课:课前准备

      硬件设备:笔记本电脑

      熟练使用电脑的同学可以跳过这一步,小白购置笔记本电脑建议如下:

      1. cpu 8核以上,内存16G以上,存储空间512G以上
      2. 品牌尽量不要选苹果,正常品牌windows系统均可,如联想、惠普、华为、小米,华为暂时不选鸿蒙系统,不要买小众品牌
      3. 不要选择游戏类笔记本,尽量选择轻薄办公型笔记本
      4. 3000~5000均可,不需要买太贵的电脑

      软件安装:DEVC++

      根据电脑的系统下载安装软件,不清楚自己电脑系统信息的同学可以直接安装32位软件,不影响学习:

      安装流程

      1. 双击下载好的软件安装包
      2. 等待安装程序加载
      3. 选择语言,默认English
      4. 同意安装,默认I Agree
      5. 选择需要安装的插件,默认Next
      6. 选择安装路径,默认安装C盘,点击Install。熟练使用电脑的同学自行选择路径,不熟练的默认路径即可。
      7. 等待安装完成,点击Next。
      8. 点击Finish,完成安装
      9. 第一次开启软件,将软件语言调整为简体中文,点击Next。
      10. 确认软件显示样式,点击Next。
      11. 软件初始化完成,点击OK。
      12. 安装成功,展示软件主页。

      视频教程

      账号注册:信奥一本通网站

      进入网站:信奥一本通

      1. 右上角注册新用户,先输入邮箱,获取验证码,将验证码填入输入框,再输入用户名、密码等信息提交
      2. 用户登录->个人中心->加入教师团队[教师用户名:fangys,加入验证码:123456]

      账号注册:房老师学习网站

      进入网站:FangT

      1. 右上角用户,进入用户终端,依次输入register、用户名、密码注册成功
      2. 课程相关内容均在资料中可以找到

      准备工作到此结束


      DEV软件测试

      启动软件,创建源文件

      1. 点击左上角“文件”按钮,选择“新建”,在选择“源代码”即可。熟悉电脑的同学可以使用Ctrl+N新建。
      2. 在软件“未命名1”下方的空白处填写代码,具体代码如下
      #include <iostream>
      using namespace std;
      int main(){
          cout<<"Hello FangOS";
          return 0;
      }
      1. 编写完代码后,点击左上角“文件”,选择“保存”。快捷键:Ctrl+S
      2. 选择保存在:“桌面”,点击“保存”按钮。
      3. 保存以后,原本“[*]未命名1”变成“未命名1.cpp”,则保存成功。
      4. 点击“运行”,选择“编译”。
      5. 等下下方提示:“错误0,输出文件…exe”,说明编译成功
      6. 点击“运行”,选择“运行”
      7. 弹出窗口,窗口显示内容“Hello FangOS”,与代码内容一致,则运行成功
      8. 再次重复以上内容,先修改代码,如下:
      #include <iostream>
      using namespace std;
      int main(){
          cout<<"Hello FangOS";
          cout<<endl;
          cout<<"Hello,China";
          return 0;
      }
      1. 再次编译->运行,黑框中显示具体内容即为成功。
      2. 测试难度升级,将代码更改为以下内容:
      #include <iostream>
      using namespace std;
      int main(){
          int a,b;
          cin>>a>>b;
          cout<<a+b;
          return 0;
      }
      1. 编译运行程序后,显示黑框,在黑框内输入两个数字,用空格隔开,输入完第二个数字后,按回车,如果显示的新数字确实是输入的两个数字之和,说明编写成功。如:输入3 空格 5 回车,黑框第二行自动显示一个数字8,恰好是3+5的和。

    44. HTML:初识框架及常用标签

      基础常识

      网页的本质:文本文件

      网页=后缀名为.html的纯文本文件

      浏览器=翻译官(把文本文件翻译成界面)

      HTML=超文本标记语言

      环境搭建与HTML骨架

      开发工具

      VSCode

      基础框架

      <!DOCTYPE html>
      <html>
      <head>
          <meta charset="UTF-8">
          <title>我的第一个网页</title>
      </head>
      <body>
          欢迎来到我的网页世界!
      </body>
      </html>

      解析

      <!DOCTYPE html>:声明这是HTML5

      <html>:所有代码的大容器,网页的根

      <head>:网页的“大脑”,放置不显示在页面的信息

      <meta charset=”UTF-8″>:使网页支持中文

      <title>:浏览器标签栏上显示的内容

      <body>:网页的主体内容,显示在页面上的具体内容

      核心标签

      一:块级元素与行内元素

      块级元素:独占一行,宽度100%。如:<h1>~<h6>,<p>,<div>。

      行内元素:多个排在一行,宽高由内容撑开。如:<span>,<strong>,<em>。

      <body>
          <!-- 1. 标题标签:从大到小,用于文章标题 -->
          <h1>一级标题:网页大标题</h1>
          <h2>二级标题:章节标题</h2>
          <h3>三级标题:小节标题</h3>
          
          <!-- 2. 段落标签 -->
          <p>这是一个段落。段落之间有自动的间距,阅读更舒服。</p>
          <p>这是第二个段落。HTML会忽略代码中的多个空格和换行。</p>
          
          <!-- 3. 强调标签(行内) -->
          <p>这句话里有<strong>加粗强调</strong>和<em>斜体强调</em>。</p>
          
          <!-- 4. 无语义的容器:div(块) 和 span(行内) -->
          <div>我是一个块,独占一行</div>
          <span>我是行内1</span><span>我是行内2</span>
      </body>

      二:图片与路径

      图片:<img src=”图片地址” alt=”图片描述”>

      路径:

      1. 网络图片:src="https://picsum.photos/200/150" (演示用随机图片)。
      2. 本地图片:相对文件夹,相对路径
      3. alt属性:当图片加载失败时显示的文字,对盲人读屏软件友好。

      三:超链接与列表

      超链接:<a href=”网址”>点击文字</a>

      <!-- 链接到外部网站(记得带http://) -->
      <a href="https://www.baidu.com" target="_blank">打开百度</a>
      <!-- target="_blank" 表示在新标签页打开,非常重要 -->
      
      <!-- 链接到页面内的某个位置(锚点) -->
      <h2 id="section1">第一部分</h2>
      <a href="#section1">回到第一部分</a>

      列表:

      <!-- 无序列表:导航菜单常用 -->
      <ul>
          <li>首页</li>
          <li>关于我们</li>
          <li>联系方式</li>
      </ul>
      
      <!-- 有序列表:步骤说明 -->
      <ol>
          <li>打开编辑器</li>
          <li>新建文件</li>
          <li>保存为.html</li>
      </ol>

    45. 2061:【例1.2】梯形面积

      #include  <iostream>
      using  namespace  std;
      int  main(){
          cout<<"400.00";
      }
    46. 2060:【例1.1】计算机输出

      #include <iostream>
      using namespace std;
      int main()
      {
          cout << "Hello World!" << endl;
          return 0;
      }
    47. 1000:入门测试题目

      #include <iostream>
      using namespace std;
      int main(){
          int a,b;
          cin>>a>>b;
          cout<<a+b;
          return 0;
      }
    48. 二分法(Bisection Method)

      二分法是一种非常基础且通用的数值计算方法,主要用于求解方程的根(即找到使函数值为 0 的点)。

      核心原理:夹逼定理

      二分法的理论基础是连续函数的介值定理(Intermediate Value Theorem)。

      简单来说:

      如果一个函数 f(x) 在区间 [a,b] 上是连续的,并且 f(a) 和 f(b) 的符号相反(即一正一负,意味着图像穿过了 x 轴),那么在这个区间内部至少存在一个点 c,使得 f(c)=0。

      此时,我们只需要不断的找中心点,通过比较c在中心点左还是右来减小区间,最终确定c的具体位置。

      从数学角度来讲,由于这个方法的鲁棒性强,因此我们可以把它当作解决求根的最后一个方案。

      二分查找

      核心思想:在有序的数组里,每次砍掉一半不可能的区间,快速找到目标。

      前提:数组必须有序。

      步骤:

      1. 设左边界 left,右边界 right
      2. 取中点 mid = (left + right) / 2
      3. 比较 a[mid] 和目标值:
        • 等于 → 找到了
        • 小于 → 答案在右边,left = mid + 1
        • 大于 → 答案在左边,right = mid - 1
      4. 重复直到找到或区间为空

      时间复杂度:O(log n)

      100 万的数据最多查 20 次。

      应用场景

      • 找某个数是否存在
      • 找第一个 ≥ x 的位置(lower_bound)
      • 找最后一个 ≤ x 的位置

      二分答案

      核心思想:答案本身具有单调性,先二分答案的值,再通过函数判断该值是否正确。

      步骤:

      • 最大的最小 xxx
      • 最小的最大 xxx
      • 满足条件的最小值
      • 满足条件的最大值
      • 分割数组、最小最大距离、最小花费等

      应用场景

      1. 先确定题目要求的内容
      2. 根据求的内容确定答案的区间[L,R]
      3. 根据当前区间,二分出答案mid
      4. 写一个check(mid)函数:
        • 返回true:尝试更优答案
        • 返回false:调整方向
      5. 通过不断缩小区间最后确定最优解

      代码相关

      确定答案范围:L和R
      初始化ans:-1、L或R
      初始化mid:作为区间中心点
      
      while (L <= R) { 
          mid = L + (R - L) / 2;
          if (check(mid)) {  // 满足条件
              ans = mid;
              R = mid - 1;  // 尝试更小
          }
          else {
              L = mid + 1;   // 不够,要更大
          }
      }

      算法库相关

      查找value是否存在

      • bool binary_search(first, last, value);

      查找第一个大于等于value的位置

      • iterator lower_bound(first, last, value);

      查找第一个大于value的位置

      • iterator upper_bound(first, last, value);

      同时返回lower和upper

      • pair<iterator, iterator> equal_range(…);
      • 常用于求value出现的次数
    49. 分治算法(Divide and Conquer)

      分治算法就是先“分”,再“治”,最后“合”。

      核心思想

      把一个复杂的大问题,拆成若干个结构相同的小问题,分别解决后,再把结果合并,得到原问题的答案。

      步骤

      1. 分解(Divide):把原问题拆分成若干个规模更小、结构相同的子问题。
      2. 解决(Conquer):子问题足够简单时,直接求解;否则递归继续分治。
      3. 合并(Combine):将子问题的结果合并,得到原问题的解。

      使用场景

      • 问题可以清晰拆分
      • 子问题相互独立(无过多重叠,否则更适合动态规划)
      • 子问题解可以高效合并

      经典应用

      1. 归并排序:把数组切开,分别递归排序,最后合并成有序数组
      2. 快速排序:选基准将数组分成两部分,分别递归排序,天然有序无需额外合并
      3. 二分法、三分法
      4. 大数乘法、矩阵乘法
      5. 求逆序对、最近点对

      常用递推式

      T(n)=aT(n​/b)+f(n)

      • a:子问题个数
      • b:每次规模缩小倍数
      • f(n):分解 + 合并的耗时

      常见复杂度

      • 归并排序:O(nlogn)
      • 二分查找:O(logn)
      • 快速排序:平均 O(nlogn),最坏 O(n^2)

      与递归、动态规划的对比

      分治:子问题独立,不重复计算

      动态规划:子问题重叠,用缓存避免重复

      递归:只是实现方式,分治常靠递归实现

      总结

      大事化小,小事解决,结果合并。

    50. 素数筛:欧拉筛

      欧拉筛是埃氏筛的优化版本,核心思想是每个合数只被它的最小质因子筛掉一次,从而实现真正的线性时间复杂度 O(n)。

      基本原理

      每个合数只会被它的最小质因子筛掉。

      实现要点:

      1. 维护一个素数列表 primes
      2. 遍历每个数 i(从2到n)
      3. 用当前素数列表中的素数 p 与 i 相乘,标记合数
      4. 当 i % p == 0 时停止(保证最小质因子原则)

      基础实现

      const int N = 105;
      bool isPrime[N] = {0, 0};
      for (int i = 2; i < N; i++) isPrime[i] = true;
      int primes[N], t = 0; // 质数表
          
      for (int i = 2; i <= n; i++) {
          if (isPrime[i]) {
              primes[t++] = i;  // i是素数,加入列表
          }
          // 用当前素数去标记合数
          for (int p = 0; p < t && i * p <= n; p++) {
              isPrime[i * primes[p]] = false;  // 标记合数
              // 关键:如果p是i的最小质因子,停止
              if (i % primes[p] == 0) break;
          }
      }

      标准实现

      vector<int> eulersieve(int n) {
          vector<bool> isPrime(n + 1, true);
          vector<int> primes;
          isPrime[0] = isPrime[1] = false;
          for (int i = 2; i <= n; i++) {
              if (isPrime[i]) {
                  primes.push_back(i);  // i是素数,加入列表
              }
              // 用当前素数去标记合数
              for (int p : primes) {
                  if (i * p > n) break;  // 超出范围
                  isPrime[i * p] = false;  // 标记合数
                  // 关键:如果p是i的最小质因子,停止
                  if (i % p == 0) break;
              }
          }
          return primes;
      }
      vector<int> primes = eulersieve(n);

      时间复杂度与空间复杂度

      • 时间复杂度:O(n)
      • 空间复杂度:O(n)

      应用场景

      1. 快速分解质因数

      2. 线性筛欧拉函数 φ(n)

      3. 线性筛莫比乌斯函数 μ(n)

      4. 线性筛约数个数 d(n)

      埃氏筛 vs 欧拉筛

      特性埃氏筛欧拉筛
      时间复杂度O(n log log n)O(n)
      常数因子较大
      空间复杂度O(n)O(n)
      实现复杂度简单中等
      实际速度(n≤1e7)略快
      实际速度(n≤1e8)较慢
      可扩展性容易并行化难以并行化
      额外信息仅素数判断可获得最小质因子等

      选择建议

      1. n ≤ 10^7:两者差别不大,选埃氏筛(代码简单)
      2. n > 10^7:选欧拉筛(真正的线性)
      3. 需要最小质因子:必须用欧拉筛
      4. 需要筛积性函数(欧拉函数、莫比乌斯函数等):必须用欧拉筛
      5. 内存受限:埃氏筛可用 bitset 优化

    51. 素数筛:埃氏筛

      埃拉托斯特尼筛法:常用于判断1~n之间的数字是否是质数。

      基本原理

      1. 从2开始,将每个素数的倍数标记为合数
      2. 未被标记的数就是素数

      步骤:

      1. 初始化1~n之间所有的数字,默认“是”质数
      2. 从i=2开始遍历,只要i*i<=n就继续向后遍历,如果当前数字不是质数就跳过,如果当前是质数:
        • 从i的2倍开始向后遍历i的所有倍数,并标记为合数
      3. 当循环结束时,所有1~n之间的数字都已经标记成了正确的质数或合数

      优化:从  开始标记,因为更小的倍数已经被更小的素数筛过了。

      基础实现

      const int N = 105;
      bool isPrime[N] = {0, 0};
      // 假设除了0和1,其他都是质数
      for (int i = 2; i < N; i++) isPrime[i] = true; 
      
      int n;
      cin>>n;
      // 从2开始,将所有质数的倍数标记成不是质数
      for (int i = 2; i * i <= n; i++) {
          if (isPrime[i]) {
              // 从 i*i 开始标记
              for (int j = i * i; j <= n; j += i) {
                  isPrime[j] = false;
              }
          }
      }

      标准实现

      vector<bool> sieve(int n) {
          vector<bool> isPrime(n + 1, true);
          isPrime[0] = isPrime[1] = false;
          
          for (int i = 2; i * i <= n; i++) {
              if (isPrime[i]) {
                  // 从 i*i 开始标记
                  for (int j = i * i; j <= n; j += i) {
                      isPrime[j] = false;
                  }
              }
          }
          
          return isPrime;
      }
      vector<bool> primes = sieve(n);

      时间复杂度与空间复杂度

      • 时间复杂度:O(n log log n),非常接近线性
      • 空间复杂度:O(n)

      应用场景

      1. 统计素数个数

      2. 分解质因数(预处理最小质因子)

      3. 区间筛(筛大区间内的素数)

      4. 欧拉函数预处理

    52. 素数筛:欧拉筛的更多应用

      1.记录最小质因子

      在欧拉筛中,每个合数被它的最小质因子筛掉。我们可以利用这个特性,在筛的过程中记录每个数的最小质因子 lp[x](least prime factor)。

      void euler_sieve(int n) {
          for (int i = 2; i <= n; i++) {
              if (!lp[i]) {
                  lp[i] = i;
                  primes.push_back(i);
              }
              for (int p : primes) {
                  if (p > lp[i] || i * p > n) break;
                  lp[i * p] = p;
              }
          }
      }

      2.利用最小质因子分解质因数

      有了 lp[x],我们可以快速O(logx))分解任意 x 的质因数:

      vector<pair<int, int>> factorize(int x) {
          vector<pair<int, int>> factors;
          while (x > 1) {
              int p = lp[x];
              int cnt = 0;
              while (x % p == 0) {
                  x /= p;
                  cnt++;
              }
              factors.push_back({p, cnt});
          }
          return factors;
      }

      时间复杂度O(质因子个数),最坏 O(logn)(因为每次至少除以 2)。

    53. P1604 B进制星球

      本题长度超过正常范围,所以无法通过转十进制计算再转回。

      模拟数学竖式计算,逢十进一变成逢B进一(高精度加法)。

      #include <bits/stdc++.h>
      using namespace std;
      int B;
      string a,b,c;
      int num[128]={};
      char sig[128]={};
      void init(){
          for(int i='0';i<='9';i++) num[i]=i-'0';
          for(int i='A';i<='Z';i++) num[i]=i-'A'+10;
          for(int i=0;i<=9;i++) sig[i]='0'+i;
          for(int i=10;i<36;i++) sig[i]='A'+i-10;
      }
      int main(){
          init(); // 初始化
          cin>>B;
          cin>>a>>b;
          int jw=0;
          for(int i=a.size()-1,j=b.size()-1;i>=0||j>=0;i--,j--){
              if(i<0) a[i=0]='0';
              if(j<0) b[j=0]='0';
              int t=num[a[i]]+num[b[j]]+jw;
              jw=t/B;
              c+=sig[t%B];
          }
          if(jw) c+=to_string(jw);
          while(c.back()=='0'&&c.size()>1) c.resize(c.size()-1);
          reverse(c.begin(),c.end());
          cout<<c;
      }
    54. [NOIP 2006 普及组] 明明的随机数

      可以利用set的有序和去重的特性直接完成这道题

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,x;
          set<int> s;
          cin>>n;
          while(n--){
              cin>>x;
              s.insert(x);
          }
          cout<<s.size()<<endl;
          for(int i:s) cout<<i<<" ";
          return 0;
      }
    55. P4018 Roy&October之取石子

      博弈:只要是6的倍数就是第二个人赢,否则第一个人赢。

      原因:当n=6时,第一个人可以取1-5个,无论怎么取,第二个人可以取走剩下的所有,必赢。当n大于6时,第一个人只需要每次取完后将石子控制在6的倍数,则攻守互换,第一个人变成6个的后手,必赢。

      #include <bits/stdc++.h>
      using namespace std;
      int main(){
          int n,a;
          cin>>n;
          while(n--){
              cin>>a;
              cout<<(a%6==0?"Roy wins!":"October wins!")<<endl;
          }
          return 0;
      }
    56. P1871 对撞机

      通过线性筛标记所有分解质因数

      #include <bits/stdc++.h>
      using namespace std;
      const int MAXN=1e5+5;
      int n,m;
      bool running[MAXN];          // 是否运行
      int prime_owner[MAXN];       // 占用情况
      int isp[MAXN];               // 筛标
      vector<int> primes;          
      void init(){
          for(int i=2;i<=n;i++){
              if(isp[i]==0){
                  isp[i]=i;
                  primes.push_back(i);
              }
              for(int p:primes){
                  if(p*i>n) break;
                  isp[p*i]=p;
                  if(i%p==0) break;
              }
          }
      }
      vector<int> factorize(int x) {
          vector<int> res;
          while(x>1){
              int p=isp[x];
              res.push_back(p);
              while(x%p==0) x/=p;
          }
          return res;
      }
      int main(){
          ios::sync_with_stdio(false);
          cin.tie(0);
          cin>>n>>m;
          init();
          while(m--){
              char op;
              int x;
              cin>>op>>x;
              if(op=='+'){
                  if(running[x]){
                      cout<<"Already on\n";
                      continue;
                  }
                  vector<int> factors=factorize(x);
                  int t=-1;
                  for(int p:factors)
                      if(prime_owner[p]!=0){
                          t=prime_owner[p];
                          break;
                      }
                  if(t!=-1) cout<<"Conflict with "<<t<<"\n";
                  else {
                      running[x]=true;
                      for(int p:factors) prime_owner[p]=x;
                      cout<<"Success\n";
                  }
              } 
              else {
                  if(!running[x]) cout<<"Already off\n";
                  else {
                      running[x]=false;
                      vector<int> factors=factorize(x);
                      for(int p:factors)
                          if(prime_owner[p]==x) prime_owner[p]=0;
                      cout<<"Success\n";
                  }
              }
          }
          return 0;
      }
    57. P1835 素数密度

      直接求素数容易超时,反向求合数,再做减法

      #include <bits/stdc++.h>
      using namespace std;
      bool isp[500005]={1,1};
      int p[500005],t=0,l,r;
      int tong[1000005];
      void init(){
          for(int i=2;i<500005;i++){
              if(!isp[i]) p[t++]=i;
              for(int j=0;j<t&&p[j]*i<500005;j++){
                  isp[p[j]*i]=1;
                  if(i%p[j]==0) break;
              }
          }
      }
      int main(){
          init();
          cin>>l>>r;
          for(int i=0;i<t;i++){
              if(p[i]>r) break;
              if(p[i]>=l) continue;
              for(long long j=max(2,(l+p[i]-1)/p[i]);j*p[i]<=r;j++)
                  tong[j*p[i]-l]++;
          }
          int ans=0;
          for(int i=0;i<=r-l;i++)
              if(tong[i]==0) ans++;
          if(l==1) ans--;
          cout<<ans;
          return 0;
      }
    58. P1865 A % B Problem

      容易超时,需要预处理素数表

      #include <bits/stdc++.h>
      using namespace std;
      bool isp[1000005]={true,true};
      int n,m,l,r,sum[1000005];
      void init(){
          for(int i=2;i<=1000;i++){
              if(!isp[i]){
                  for(int j=i*i;j<=1000000;j+=i){
                      if(!isp[j]) isp[j]=true;
                  }
              }
          }
          for(int i=2;i<=1000000;i++){
              if(!isp[i]) sum[i]=sum[i-1]+1;
              else sum[i]=sum[i-1];
          }
      }
      int main(){
          init();
          cin>>n>>m;
          while(n--){
              cin>>l>>r;
              if(l>=1&&l<=m&&r>=1&&r<=m) cout<<sum[r]-sum[l]+!isp[l]<<endl;
              else cout<<"Crossing the line\n";
          }
          return 0;
      }
    59. [蓝桥杯 2024 国 C] 六一儿童节

      如果p是一个质数,而a是与p互质的任一整数,则有a^(p-1)≡1(mod p)。

      费马小定理

      本题6421是一个质数,根据费马小定理:如果一个数 a 不是 6421 的倍数,那么 a 的 6420 次方除以 6421 的余数是 1。

      这就意味着:a 的次方,只看指数除以 6420 的余数就够了。

      也就是说,要计算x^x除以6421的余数,只需要看底数x除以6421的余数,指数x除以6420的余数。

      #include<bits/stdc++.h>
      using namespace std;
      long long ans,a[6500],b[6500];
      long long qpow(long long a,long long b){
      	long long ans=1;
      	while(b){
      		if(b&1) ans=ans*a%6421;
      		b/=2;
      		a=a*a%6421;
      	}
      	return ans;
      }
      int main(){
              // 计算 x^(x-1) mod 6421 的数量
      	for(int i=1;i<=20240601;i++) a[qpow(i%6421,i%6420)]++;
      	for(int i=0;i<6421;i++){
      		int j=(6421-i)%6421;
                      if(i<j) ans+=a[i]*a[j];
                      else if(i==j) ans+=a[i]*(a[i]-1)/2;
      	}
      	printf("%lld",ans);
      	return 0;
      }
    60. [USACO22FEB] Sleeping in Class B

      #include <bits/stdc++.h>
      using namespace std;
      int t,n,a[100005];
      int main(){
          cin>>t;
          while(t--){
              cin>>n;
              int sum=0,ans=0;
              for(int i=1;i<=n;i++){
                  scanf("%d",&a[i]);
                  sum+=a[i];
              }
              for(int i=n;i>=1;i--){
                  if(sum%i) continue;
                  bool f=true;
                  int t=0;
                  for(int j=1;j<=n;j++){
                      t+=a[j];
                      if(t>sum/i){
                          f=false;
                          break;
                      }
                      else if(t==sum/i) t=0;
                  }
                  if(f){
                      cout<<n-i<<endl;
                      break;
                  }
              }
          }
          return 0;
      }
    61. [USACO2.3] 零的数列Zero Sum

      数据不大,可以暴力

      #include<bits/stdc++.h>
      using namespace std;
      int n;
      bool judge(string s){
          int ans=0;
          int num=0;
          char op='+';
          for(int i=0;i<=s.length();i++) {
              if(s[i]==' ') continue;
              char c=s[i];
              if(isdigit(c)) num=num*10+(c-'0');
              else {
                  if(op=='+') ans+=num;
                  else if(op=='-') ans-=num;
                  op=c;
                  num=0;
              }
          }
          return ans==0;
      }
      void dfs(int k,string s){
          if(k==n){
              s+=to_string(k);
              if(judge(s)) cout<<s<<"\n";
          }
          else{
      	dfs(k+1,s+to_string(k)+" ");
      	dfs(k+1,s+to_string(k)+"+");
      	dfs(k+1,s+to_string(k)+"-");
          }
      }
      int main(){
          cin>>n;
          dfs(1,"");
          return 0;
      }
    62. [蓝桥杯青少年组省赛2022] 组合

      已知 a 和 b 为大于1的正整数,且 gcd(a,b)=1 ,则使不定方程 ax+by=C 不存在非负整数解的最大整数为 C=a×bab

      塞瓦维斯特定理
      #include <bits/stdc++.h>
      using namespace std;
      int n,m,a[10005];
      int main(){
          cin>>n>>m;
          for(int i=0;i<=m;i++){
              for(int j=0;j<=n;j++){
                  if(i*n+j*m>n*m) break;
                  a[i*n+j*m]=1;
              }
          }
          for(int i=n*m;i>=1;i--)
              if(a[i]==0){
                  cout<<i;
                  break;
              }
          return 0;
      }
    63. P1498 南蛮图腾

      数据范围很小,可以暴力解决问题

      #include <bits/stdc++.h>
      using namespace std;
      char a[2050][2050];
      int n;
      void draw(int x,int y,int d) {
          if(d==1){
              a[x][y+1]='/';
              a[x][y+2]='\\';
              a[x+1][y]='/';
              a[x+1][y+1]='_';
              a[x+1][y+2]='_';
              a[x+1][y+3]='\\';
              return;
          }
          int step=1<<(d-1); // 2^(d-1)
          draw(x,y+step,d-1); // 上面
          draw(x+step,y,d-1); // 左下
          draw(x+step,y+2*step,d-1); // 右下
      }
      int main(){
          cin>>n;
          memset(a,' ',sizeof(a));
          draw(0,0,n);
          int h=1<<n;
          int w=1<<(n+1);
          for (int i=0;i<h;i++) {
              for (int j=0;j<w;j++)
                  cout<<a[i][j];
              cout<<endl;
          }
          return 0;
      }
    64. P1168 中位数

      中位数是一组数据按大小顺序排列后,位于中间位置的数。

      1. 数据个数为奇数:直接取最中间的那个数。
      例如1, 3, 5, 7, 9,中位数 = 5(第3个数)
      2. 数据个数为偶数:取中间两个数的平均值。
      例如1, 3, 5, 7,中位数 = (3 + 5) / 2 = 4

      反复排序取中间数字会超时!!!

      #include <bits/stdc++.h>
      using namespace std;
      int n,x;
      priority_queue<int> l;
      priority_queue<int,vector<int>,greater<int>> r;
      int main(){
          cin>>n;
          for(int i=1;i<=n;i++){
              scanf("%d",&x);
              if(l.empty()||x<=l.top()) l.push(x);
              else r.push(x);
              // 保持平衡
              if(l.size()>r.size()+1){
                  r.push(l.top());
                  l.pop();
              } 
              else if(r.size()>l.size()){
                  l.push(r.top());
                  r.pop();
              }
              if(i%2) printf("%d\n",l.top());
          }
          return 0;
      }
    65. P1228 地毯填补问题

      #include <bits/stdc++.h>
      using namespace std;
      int k,x,y,n;
      int pow2(int k){
          int sum=1;
          while(k--) sum*=2;
          return sum;
      }
      void f(int x,int y,int a,int b,int l)
      {
          if(l==1) return;
          if(x-a<=l/2-1 && y-b<=l/2-1){
              printf("%d %d 1\n",a+l/2,b+l/2);
              f(x,y,a,b,l/2);
              f(a+l/2-1,b+l/2,a,b+l/2,l/2);
              f(a+l/2,b+l/2-1,a+l/2,b,l/2);
              f(a+l/2,b+l/2,a+l/2,b+l/2,l/2);
          }
          else if(x-a<=l/2-1 && y-b>l/2-1){
              printf("%d %d 2\n",a+l/2,b+l/2-1);
              f(a+l/2-1,b+l/2-1,a,b,l/2);
              f(x,y,a,b+l/2,l/2);
              f(a+l/2,b+l/2-1,a+l/2,b,l/2);
              f(a+l/2,b+l/2,a+l/2,b+l/2,l/2);
          }
          else if(x-a>l/2-1 && y-b<=l/2-1){
              printf("%d %d 3\n",a+l/2-1,b+l/2);
              f(a+l/2-1,b+l/2-1,a,b,l/2);
              f(a+l/2-1,b+l/2,a,b+l/2,l/2);
              f(x,y,a+l/2,b,l/2);
              f(a+l/2,b+l/2,a+l/2,b+l/2,l/2);
          }
          else{
              printf("%d %d 4\n",a+l/2-1,b+l/2-1);
              f(a+l/2-1,b+l/2-1,a,b,l/2);
              f(a+l/2-1,b+l/2,a,b+l/2,l/2);
              f(a+l/2,b+l/2-1,a+l/2,b,l/2);
              f(x,y,a+l/2,b+l/2,l/2);
          }
      }
      int main()
      {
          cin>>k>>x>>y;;
          n=pow2(k);
          f(x,y,1,1,n);
          return 0;
      }
      
    66. P1182 数列分段 Section II

      经典的二分答案:段和最大值最小为多少

      #include <bits/stdc++.h>
      using namespace std;
      int n,m;
      long long a[100005];
      bool judge(long long mid){
          long long sum=a[1],cnt=1;
          for(int i=2;i<=n;i++){
              if(sum+a[i]<=mid){
                  sum+=a[i];
              }
              else{
                  sum=a[i];
                  cnt++;
                  if(cnt>m) return false;
              }
          }
          return true;
      }
      int main(){
          cin>>n>>m;
          long long l=0,r=0;
          for(int i=1;i<=n;i++){
              scanf("%lld",&a[i]);
              if(l<a[i]) l=a[i];
              r+=a[i];
          }
          long long ans=0;
          while(l<=r){
              long long mid=l+(r-l>>1);
              if(judge(mid)){
                  ans=mid;
                  r=mid-1;
              }
              else l=mid+1;
          }
          cout<<ans;
          return 0;
      }
    67. [GESP202603 五级] 找数

      对于40%的数据,可以直接双重for循环解决
      对于100%的数据,可以排序+双指针解决,也可以输入数组B时直接判断

      方案1

      #include <bits/stdc++.h>
      using namespace std;
      
      int main() {
          int n, m, A[100005], B[100005];
          cin >> n >> m;
      
          for (int i = 0; i < n; i++) cin >> A[i];
          for (int i = 0; i < m; i++) cin >> B[i];
      
          int ans = 0;
          for (int i = 0; i < n; i++) {
              for (int j = 0; j < m; j++) {
                  if(A[i] == B[j]) ans++;
              }
          }
          
          cout << ans;
          return 0;
      }

      方案2

      #include <bits/stdc++.h>
      using namespace std;
      
      int main() {
          int n, m, x;
          cin >> n >> m;
      
          vector<int> A, B;
          for (int i = 0; i < n; i++) {
              cin >> x;
              A.push_back(x);
          }
      
          for (int i = 0; i < m; i++) {
              cin >> x;
              B.push_back(x);
          }
          
          sort(A.begin(), A.end());
          sort(B.begin(), B.end());
          
          int p = 0, q = 0, ans = 0;
          while (p < A.size() && q < B.size()) {
              if (A[p] < B[q]) {
                  p++; continue;
              }
              if (A[p] > B[q]) {
                  q++; continue;
              }
              ans++; p++; q++;
          } 
          
          cout << ans;
          return 0;
      }

      方案3

      #include <bits/stdc++.h>
      using namespace std;
      
      int main() {
          int n, m, x;
          cin >> n >> m;
      
          set<int> setA;
          for (int i = 0; i < n; i++) {
              cin >> x;
              setA.insert(x);
          }
      
          int ans = 0;
          for (int i = 0; i < m; i++) {
              cin >> x;
              if (setA.count(x)) ans++;
          }
      
          cout << ans;
          return 0;
      }
    68. [GESP202603 五级] 有限不循环小数

      终止数:分解质因数后只存在2和5的数字

      数论

      由以上定义,我们可以先将10^6以内所有符合条件的数字找出来,再从题目要求的范围[L,R]中统计出具体的个数。

      #include <bits/stdc++.h>
      using namespace std;
      
      int main() {
          int l, r;
          cin >> l >> r;
          const int MAX = 1e6;
          set<int> nums; // 排序并去重
      
          // 生成所有不超过 MAX 的形如 2^a * 5^b 的数
          for (long long a = 1; a <= MAX; a *= 2) {
              for (long long b = a; b <= MAX; b *= 5) {
                  nums.insert(b);
              }
          }
      
          // 统计个数
          int ans = 0;
          for (int t: nums) {
              if (t > r) break; 
              ans += t >= l;
          }
      
          cout << ans << endl;
          return 0;
      }

      如果本题为多组数据,则需要加入二分查找等优化方案提高求解效率。