【大合集】前期算竞常用基础算法板子和注意事项![建议收藏]

介绍

本文包括但不限于stl(主要在主页另一篇帖子里),快速幂,二分答案,二分,差分与前缀和,双指针,字符串操作,贪心,各种小细节等等,非常适合前期入门使用。

二分查找模板

函数实现
#include <iostream>
using namespace std;

// 二分查找函数,查找目标值是否存在
int bsearch(int *a, int n, int x) { // 数组a,长度n,目标值x
    int l = 1, r = n;              // 左边界l,右边界r
    while (l <= r) {               // 循环直到左右边界重叠或越界
        int m = (l + r) / 2;       // 中间位置m
        if (a[m] == x) return m;   // 找到目标值返回下标
        else if (a[m] < x) l = m + 1; // 目标值在右区间
        else r = m - 1;            // 目标值在左区间
    }
    return -1;                     // 未找到目标值返回-1
}

int main() {
    int n = 6;                     // 数组长度
    int a[] = {0, 2, 4, 6, 8, 10, 12}; // 数组,a[0]占位
    int x = 8;                     // 要查找的目标值
    int pos = bsearch(a, n, x);    // 调用二分查找函数
    if (pos != -1) cout << "Found at: " << pos << endl;
    else cout << "Not found" << endl;
    return 0;
}
非函数实现
#include <iostream>
using namespace std;

int main() {
    int n = 6;                     // 数组长度
    int a[] = {0, 2, 4, 6, 8, 10, 12}; // 数组,a[0]占位
    int x = 8;                     // 要查找的目标值
    int l = 1, r = n;              // 左边界l,右边界r
    int pos = -1;                  // 存储目标值下标,默认-1表示未找到
    while (l <= r) {               // 循环直到左右边界重叠或越界
        int m = (l + r) / 2;       // 中间位置m
        if (a[m] == x) {           // 找到目标值
            pos = m;               // 记录下标
            break;                 // 退出循环
        } else if (a[m] < x) l = m + 1; // 目标值在右区间
        else r = m - 1;            // 目标值在左区间
    }
    if (pos != -1) cout << "Found at: " << pos << endl;
    else cout << "Not found" << endl;
    return 0;
}
二分答案法经典题目实现形式

二分答案法的步骤:
确定搜索区间:根据题目要求,设定一个可能的答案范围。
条件判断函数:定义一个函数 ceii(mid),判断在当前值 mid 时,是否满足条件。
二分查找:使用二分法在答案空间中查找满足条件的值。
如果 check(mid) 返回 true,说明当前 mid 可能是一个可行解,尝试更大的值。
如果 check(mid) 返回 false,说明当前 mid 不是可行解,尝试更小的值。

#include<bits/stdc++.h>
using namespace std;
int n;
long long c;
const int N = 2e5 + 10;
int a[N];
long long ceii(int m){
	long long s = 0;
	for(int i = 1;i<=n;i++){
		s += (a[i] + 2LL * m) * (a[i] + 2LL * m); 
	if (s > c) return s; 
	}
	return s;
}


int main() {
	cin>>n>>c;
	for(int i = 1;i<=n;i++){
		cin>>a[i];
	}
	
	int l = 1, r = 1e6;              
    int pos = -1;                  
    while (l <= r) {               
        int m = (l + r) / 2;
		long long s  = ceii(m);  
        if (s == c) {           
            pos = m;
            break;                  
        } else if (s < c) l = m + 1; 
        else r = m - 1;            
    }

cout<<pos;
	
	

	
	
	
	return 0;
}

一维前缀和模板

#include<bits/stdc++.h>
using namespace std;

int main() {
    const int N = 1e5 + 10;
    int sum[N], a[N];
    int n, m;                   
    cin >> n >> m;              //n为数组a的长度,m为询问次数
    int l, r;
    sum[0] = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum[i] = sum[i-1] + a[i];
    }
    for (int i = 1; i <= m; i++) {
        cin >> l >> r;          //左区间和右区间
        cout << sum[r] - sum[l-1] << endl;//区间[l, r]内元素的和,非下标
    }
    return 0;
}

一维差分处理区间增量问题

#include <iostream> 
using namespace std;

int main() {
    const int N = 1e5 + 10;     //N表示数组的最大长度
    int a[N] = {0}, b[N] = {0};
    int n, m;                   //n为数组a的长度,m为询问次数
    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];            
        b[i] = a[i] - a[i - 1]; // 构建差分数组b
    }

    while (m--) {
        int l, r, c;            // l 和 r 表示操作的区间 [l, r],c 表示加的值
        cin >> l >> r >> c; 
        b[l] += c;
        if (r + 1 <= n) {       // 如果 r+1 没有越界
            b[r + 1] -= c;
        }
    }
    // 根据差分数组 b 计算最终的数组a
    for (int i = 1; i <= n; i++) {
        a[i] = a[i - 1] + b[i]; // 根据前缀和恢复原数组a
    }
    
    for (int i = 1; i <= n; i++) {
        cout << a[i] << " "; 
    }
    cout << endl;
    return 0; 
}

二维前缀和模板

#include<bits/stdc++.h>
using namespace std;

const int N = 1e9+100;
int s[N][N],a[N][N];
int main() {
    int n,m,q;
    cin>>n>>m>>q;
    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++){
            s[i][j] = s[i-1][j]+ s[i][j-1]+a[i][j] - s[i-1][j-1];//二维前缀和模板数组
        }
    }
    int x1,y1,x2,y2;
    for(int i = 1;i<=q;i++){
        cin>>x1>>y1>>x2>>y2;
        cout<<s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]<<endl; //求从x1,y1到x2,y2的区域和
    }
    return 0;
}

二维差分增量模板

#include<bits/stdc++.h>
using namespace std;

const int N = 1000 + 10;
int a[N][N], b[N][N];
int n, m, q;

void insert(int x1, int y1, int x2, int y2, int c) {     
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}

int main() {
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
            b[i][j] = a[i][j] - a[i-1][j] - a[i][j-1] + a[i-1][j-1];
        }
    }
    while (q--) {
        int x1, y1, x2, y2, c;
        cin >> x1 >> y1 >> x2 >> y2 >> c;
        insert(x1, y1, x2, y2, c);
    }
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            a[i][j] = b[i][j] + a[i-1][j] + a[i][j-1] - a[i-1][j-1];
            cout << a[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

其他常用代码片段

遍历所有排列情况
do {   } while (next_permutation(v.begin(), v.end()));
清理缓存区
cin.ignore(numeric_limits<streamsize>::max(), '\n');
经典队列操作应用题目
#include<bits/stdc++.h>
using namespace std;

int main() {
    int m, n;           //m表示缓存最大容量,n表示询问次数
    cin >> m >> n;      // 读取缓存容量 m 和页面访问次数 n
    deque<int> q(m);  // 创建一个大小为 m 的双端队列,用来模拟缓存,默认大小为 m
    int a;  // 用来存储当前访问的页面
    int cnt = 0;  // 用来记录页面缺失的次数
    int qq[1010] = {0};  // 定义一个数组 qq,长度为 1010,初始化所有元素为 0,用来记录哪些页面在缓存中
    for (int i = 1; i <= n; i++) {  // 遍历所有页面访问
        cin >> a;  // 读取当前访问的页面号
        if (!qq[a]) {  // 如果页面 a 不在缓存中
            cnt++;  // 页面缺失,增加缺页次数
            if (q.size() == m) {  // 如果缓存已满
                int x = q.front();  // 获取队列的第一个元素,即最久未使用的页面
                q.pop_front();  // 从队列中移除最久未使用的页面
                qq[x] = 0;  // 更新数组 qq,标记页面 x 不在缓存中
            }
            q.push_back(a);  // 将当前页面 a 加入队列(缓存)
            qq[a] = 1;  // 更新数组 qq,标记页面 a 已经在缓存中
        }
    }
    cout << cnt;  // 输出缺页次数
    return 0;  // 程序结束
}
字符串输入
string arr[100];
getline(cin, arr[i]);
自定义排序函数
bool cmp(类型 a, 类型 b) {
    // 第一条件: 比较 a 和 b
    if (条件1) {
        return 排序规则1;
    }
    // 第二条件: 如果第一条件不满足,比较 a 和 b
    if (条件2) {
        return 排序规则2;
    }
    // 第三条件: 如果前面所有条件都不满足,继续比较
    if (条件3) {
        return 排序规则3;
    }
    // 默认返回
    return 排序规则默认;
}

循环数组索引更新

index = (index + 1) % n; // 因为是循环,使用%运算确保数组循环

解释:
这段代码的含义是在一个固定大小为 n 的数组中循环更新 index,确保它始终保持在合法范围内(0 到 n-1)。它通过取模运算(%)实现循环行为。
具体分析:
1.index + 1:
将当前索引值 index 增加 1,表示向后移动一个位置。
2.% n:
模运算确保索引不会超出数组的边界。如果增加后索引等于或大于 n,模运算会使它“回到”开头。
例如:
2.1 如果 index + 1 = n,则 (index + 1) % n = 0。
2.2 如果 index + 1 = n + 1,则 (index + 1) % n = 1。
3.循环效果:
模运算的结果总是一个小于 n 的非负数,这样可以实现数组的循环访问。
3.1 当 index 为数组最后一个位置(n-1)时,执行 (index + 1) % n 会将索引跳转到第一个位置 0。
3.2 否则,索引会正常向后移动。

注意做题时样例输入陷阱,特殊样例,比如数组长度为0,尤其注意题目给的范围,比如>=
格式化输出
cout << setfill('0') << setw(2) << sum;
cout << fixed << setprecision(2) << num << endl;
输出32位二进制形式
cout << bitset<32>(n) << endl;
cin >> oct >> n; // 从输入以八进制形式读取一个整数
cout << dec << n; // 以十进制形式输出该整数
sawp函数不要忘了使用
注意样例空格
字符串和整数转换

以字符串的形式输入数组进行数字的运算时,字符’0’实际上是48
Example Image
注意在C++中,字符和整数之间可以进行转换。字符’0’到’9’的ASCII码分别是48到57。当你从一个字符中减去’0’时,实际上是将该字符转换为对应的数字。

注意int long范围,数组可能是double类型等等
字符数组长度
char a[100];
int b = strlen(a);
字符串长度
string a;
int b = a.size();
宏定义
#define 宏名 替换内容
定义尽量都在主函数外定义
类型别名
using ll = long long;

typedef long long ll;
结构体数组示例(贪心排序题可能用到)
#include <iostream>
#include <algorithm> // 包含 sort 函数
using namespace std;

struct Point {
    int x, y;
};

// 自定义比较函数
bool cmp(const Point &a, const Point &b) {
    if (a.x == b.x) {
        return a.y < b.y;  // 如果 x 相同,按 y 排序
    }
    return a.x < b.x;  // 否则按 x 排序
}

int main() {
    Point points[3];
    // 通过 cin 输入结构体数组
    for (int i = 0; i < 3; i++) {
        cin >> points[i].x >> points[i].y;
    }
    // 使用自定义比较函数进行排序
    sort(points, points + 3, cmp);
    // 输出排序后的数组
    for (int i = 0; i < 3; ++i) {
        cout << "points[" << i << "]: x=" << points[i].x << ", y=" << points[i].y << endl;
    }
    return 0;
}

lower_boundupper_bound

lower_bound 是 C++ 标准库 中的一个非常有用的函数,它用于在已排序的容器中查找第一个不小于(即大于或等于)给定值的元素的位置。它可以用于数组、vector、deque、set 和 map 等支持随机访问或二叉搜索的数据结构。
(容器中的元素必须是已排序的。lower_bound 使用二分查找,因此只有在排序容器中才能正确工作。)
auto 是 C++11 引入的一个关键字,用于自动推导变量的类型。通过使用 auto,编译器可以根据变量初始化时的值自动推导出该变量的类型。这使得代码更加简洁,特别是在处理复杂的类型时(例如迭代器或类型较长的容器元素)。

#include <iostream>
#include <algorithm>
using namespace std;

vector<int> vec = {1, 3, 3, 5, 7, 9};
// 查找第一个不小于 3 的位置(即第一个 3)
auto lb = lower_bound(vec.begin(), vec.end(), 3);
// 查找第一个大于 3 的位置(即第一个大于 3 的元素位置)
auto ub = upper_bound(vec.begin(), vec.end(), 3);
//lb 的位置是 vec.begin() + 1,即指向第一个 3。
//ub 的位置是 vec.begin() + 3,即指向第一个大于 3 的元素 5。
cout << "lb: " << (lb - vec.begin()) << endl;  // 输出 1,表示第一个 3
cout << "ub: " << (ub - vec.begin()) << endl;  // 输出 3,表示 5
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;

long long a[N], b[N];

int main() {
    int n, m, sum;
    cin >> n >> m >> sum;
    
    // 输入数组 a[] 和 b[]
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    for (int i = 0; i < m; i++) {
        cin >> b[i];
    }

    // 对数组 b[] 进行排序
    sort(b, b + m);

    // 遍历数组 a[],对于每个 a[i] 使用二分查找
    for (int i = 0; i < n; i++) {
        long long target = sum - a[i];
        // 在 b[] 中查找 target
        int idx = lower_bound(b, b + m, target) - b;

        // 检查是否找到并且满足 a[i] + b[idx] == sum
        if (idx < m && b[idx] == target) {
            cout << i << " " << idx << endl;
            return 0;  // 找到后直接退出
        }
    }

    // 如果没有找到符合条件的 pair
    cout << -1 << endl;
    return 0;
}

STL 常用函数

最值
max(x, y);  // 返回 x 和 y 较大值
min(x, y);  // 返回 x 和 y 较小值
排序

sort(va.begin(), va.end(), cmp);

子串操作

#include <iostream>
#include <cstring> // 包含 C 字符串处理函数 strstr
using namespace std;

int main() {
    //子串截取
    string s = "Hello, World!";
    string sub = s.substr(7, 5);    // 从下标 7 开始截取 5 个字符
    s.erase(7, 10);                 // 从下标7 删除 10 个字符

    //查找子串
    size_t pos = s.find("World");   // 查找 "World" 的位置,一般返回第一个字母起始下标
    if (pos != string::npos) {
    cout << "Found at: " << pos << endl; // 如果找到,输出位置
    }

    // 定义两个字符数组,用于存储输入的源字符串和需要查找的子串
    char str[100];                  // 源字符串
    char target[100];               // 子串
    cin.getline(str, 100);          // 使用 getline 读取一整行字符串
    cin.getline(target, 100);       // 输入需要查找的子串
    const char* pos = strstr(str, target);  // 使用 strstr 函数查找子串在源字符串中的位置
     // 判断是否找到子串
    if (pos != nullptr) {
        // 如果找到,计算子串的起始下标,并输出
        cout << "Substring found at index: " << (pos - str) << endl;  
    } else {
        cout << "Substring not found!" << endl;  
    }
    return 0; 
}

双指针模板

#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int n, x;           // n为数组大小,x为目标和
    cin >> n >> x;      // 输入n和目标和x
    int a[n + 1];       // 数组,从1开始存储
    for (int i = 1; i <= n; i++) {
        cin >> a[i];    // 输入数组
    }
    sort(a + 1, a + n + 1); // 排序数组
    int i = 1, j = n;   // 双指针初始化
    while (i < j) {
        int s = a[i] + a[j]; // 当前两个数的和
        if (s == x) {
            cout << a[i] << " " << a[j] << endl;
            break;
        } else if (s < x) {
            i++;        // 左指针右移
        } else {
            j--;        // 右指针左移
        }
    }
    if (i >= j) {
        cout << "No solution" << endl; // 没有找到满足条件的数对
    }
    return 0;
}
注意事项补充

不要忘了把oj的编译模式换成C++

一定注意输入的是n的范围还是n个整数的范围!!!!

注意输入样例形式,如1111与1 1 1 1你就要考虑不同的接收输入的方式了

别把2*i写成2i

为防止爆long long ,需对等式右边进行强转
如:s += (a[i] + 2LL * m) * (a[i] + 2LL * m);

定义cmp时,不要带等号

使用auto遍历容器时,要改变容器内元素加&,不改变不加.

continue用法示例
//数字反转,消除原数末尾0,但中间0不动.
int flag = 1;
for(int i = p-1;i>=0;i--){
    if(a[i]=='0' && flag==1 && i>0){
        continue;
    }
    flag = 0;
    cout<<a[i];

}
字符串经典操作例题(包含回文,去重,取子串)
#include<bits/stdc++.h>
using namespace std;
string s;
string zican(string sub){
	string a = "";
	a+=sub[0];
	
	for(int i = 0;i<sub.size()-1;i++){
		if(sub[i+1]!=sub[i]){
			a += sub[i+1];
		}
	}
	return a;
	
}
bool huiwen(string subb){
	int i = 0;
	int j = subb.size()-1;
	while(i<=j){
		if(subb[i]!=subb[j]){
			return false;
		}
		i++;
		j--;
	}
	return true;
}

int main() {
	long long sumj = 0;
	long long sumo = 0;
	cin>>s;
	int n = s.size();
	for(int i = 0;i<n;i++){
		for(int j = 1;i+j<=n;j++){
			string sub = s.substr(i,j);
			string subb = zican(sub);
			if(huiwen(subb)){
				if(j%2==0){
					sumo++;
				}else{
					sumj++;
				}
			}
		}
	}
	cout<<sumo<<" "<<sumj;
	
	return 0;
}
C风格字符串处理
  1. int result = strcmp(str1, str2);
    strcmp 用于比较两个 C 风格字符串(即 char 数组)
    返回 0:如果两个字符串相等。
    返回一个负整数:如果 str1 小于 str2(按字典顺序比较)。
    返回一个正整数:如果 str1 大于 str2(按字典顺序比较).

  2. strcpy 用于将一个 C 风格字符串的内容复制到另一个字符串中.
    char* strcpy(char* dest, const char* src);
    dest:目标字符数组,拷贝的结果会存储在这里。
    src:源字符数组,即你要复制的字符串。
    返回值:返回目标字符串 dest 的指针。

  3. strcat 用于将一个字符串连接到另一个字符串的末尾。
    char* strcat(char* dest, const char* src);
    dest:目标字符数组,连接结果将存储在此。
    src:源字符数组,要追加的字符串。
    返回值:返回目标字符串 dest 的指针。

  4. strchr 用于查找字符串中第一次出现指定字符的位置。
    str:要查找的字符串。
    ch:要查找的字符。
    返回值:返回指向找到的字符的指针,如果没有找到,则返回 nullptr。

#include <iostream>
#include <cstring>
using namespace std;

int main() {
    const char* str = "Hello, world!";
    char* pos = strchr(str, 'o');  // 查找字符 'o'

    if (pos != nullptr) {
        cout << "Found 'o' at position: " << (pos - str) << endl;  // 计算相对位置
    } else {
        cout << "'o' not found!" << endl;
    }

    return 0;
}

输出:Found ‘o’ at position: 4

  1. strtok 用于分割字符串,它根据指定的分隔符把一个字符串分解为多个子字符串。
    char* strtok(char* str, const char* delimiters);
    str:待分割的字符串。首次调用时需要传入原始字符串,后续调用可以传入 nullptr 来继续分割。
    delimiters:用于分割的分隔符(多个字符)。
    返回值:返回指向子字符串的指针。
#include <iostream>
#include <cstring>
using namespace std;

int main() {
    char str[] = "Hello, world, C++!";
    char* token = strtok(str, ", ");  // 以 ", " 作为分隔符

    while (token != nullptr) {
        cout << "Token: " << token << endl;
        token = strtok(nullptr, ", ");  // 继续分割
    }

    return 0;
}

输出:Token: Hello
Token: world
Token: C++

  1. strncat 类似于 strcat,但它会限制连接的字符数量,防止溢出
    dest:目标字符数组。
    src:源字符串。
    n:要连接的最大字符数。
快速幂算法
#include <iostream>
using namespace std;

// 快速幂函数:计算 a^b
int qp(int a, int b) {
    int r = 1;         // r 存储结果,初始为 1
    while (b > 0) {    // 当指数 b 大于 0 时继续循环
        if (b % 2)     // 如果 b 是奇数
            r *= a;    // 将当前基数累乘到结果
        a *= a;        // 基数自乘
        b /= 2;        // 指数减半
    }
    return r;          // 返回计算结果
}

int main() {
    int a, b;
    cout << "输入底数和指数:";
    cin >> a >> b;              // 输入底数 a 和指数 b
    cout << a << "^" << b << " = " << qp(a, b) << endl;
    return 0;
}

accumulate

vector<int> vec = {1, 2, 3, 4, 5};
// 计算 vec 中所有元素的和,初始值为 0
int sum = accumulate(vec.begin(), vec.end(), 0);
  1. 还通过提供自定义的二元操作 op,可以实现不同的聚合操作。例如,计算区间内所有元素的乘积。
vector<int> vec = {1, 2, 3, 4, 5};

 // 使用乘法作为操作符计算元素的乘积,初始值为 1
 int product = accumulate(vec.begin(), vec.end(), 1, multiplies<int>());

//multiplies<int>() 是 C++ 标准库中提供的一个函数对象,它执行乘法操作。
  1. accumulate 也可以用于其他类型的数据聚合,例如字符串的拼接。通过传入一个适当的操作函数(如加法运算符),可以将一个字符串序列拼接成一个完整的字符串。
vector<string> words = {"Hello", " ", "World", "!"};

// 使用字符串拼接操作,将所有字符串拼接起来
 string result = accumulate(words.begin(), words.end(), string());
  1. 如果我们想要找出一组元素中的最大值,也可以使用 accumulate,配合自定义的操作函数(如 std::max)。
vector<int> vec = {1, 9, 3, 7, 5};

// 使用 max 来查找最大值
int max_value = accumulate(vec.begin(), vec.end(), vec[0], max<int>());
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值