学习记录:C++常用模板(不定期持续更新)

        本文将记录个人做题时常用算法模板,供大家参考。

        本人为一名蒟蒻,如有错误,欢迎大佬指正!

一、杂项

1.代码模板

        主要内容:

        1.不开long long见祖宗,因此干脆将int机械替换为long long

        2.解绑&endl速度优化:解绑是为了提升大部分情况下cin和cout的速度,endl速度比直接输出\n要慢

        3.定义常用数组,做题快人一步;开在main外面,初始为0。

        4.应对某些OJ上题目的多组输入(如Codeforces):main内用t代表输入样例数,采用solve函数编写题解,若要跳过本组样例则直接return。

#include<bits/stdc++.h>//懒人用万能头
#define int long long
#define endl '\n'
using namespace std;
const int N=3e6+5;
int a[N];
void solve(){
    
}
signed main(){
	ios::sync_with_stdio(0); //解绑
    cin.tie(0), cout.tie(0); //解绑
	int t=1;
	//cin>>t;
	while(t--) solve();
	system("pause");
	return 0;
}

2.Pair定义与使用

//构造pair
pair<int,int> a;
//也可以写成
pair<int,pair<int,int>> p;

//支持比较:按字典序
    
//给pair赋值
a=make_pair(3,4);
a={3,4};

//访问第一个元素
a.first;
//访问第二个元素
a.second;

二、算法

1.二分板子

        来自帅哥学长的板子,方便好记。但是被隔壁大佬说拓展性不行:-( 。

int l=1,r=1e9,mid;
    int ans=0;
    while(l<=r){
	    mid=(l+r)/2;
	    if(check(mid)){
		    ans=mid;//利用ans去记录合法值
		    l=mid+1;
	    }
	    else r=mid-1;
    }
cout<<ans<<endl;

2.求最大公因数(GCD)与最小公倍数(LCM)

        虽然,C++自带了GCD与LCM函数,但是写写也无妨。

//GCD函数递归法

int gcd(int a,int b){
    if(b==0) return a;
    return gcd(b,a%b);
}

//GCD异或法

int gcd(int a,int b){
    while(a^=b^=a^=b%=a);
    return b;
}

//求最小公倍数(lcm)即两数乘积除以最大公因数(gcd)
int lcm(int a,int b){
    return a*b/gcd(a,b);
}

3.求质数

        这里提供两个板子:

        常规判断质数——isprime函数

//常规素数判断
int isprime(int x)
{
	if(x==1||x%2==0&&x!=2)return 0;
	for(int i=3;i<=sqrt(x);i+=2)//注意要等于sqrt(x)
	{
		if(x%i==0) return 0;
	}
	return 1;
}

        线性质数筛——欧拉筛

//原理:通过标记一个数的倍数,后进行遍历,没被标记的则为质数

int n=1e6;//假设求1e6内所有质数
bool a[N];//用于标记是否被筛除
int prime[N],pi=0;//prime用于存放素数,pi为prime数组的专属下标

void Eulerprime(int n){
    for(int i=2;i<=n;++i){
	    if(!a[i]) prime[++pi]=i; //++为先加一再用,先记录2,后记录质数
	    for(int j=1;prime[j]*i<=n;++j){
		    a[prime[j]*i]=1;//标记合法范围内,某一较小质数的所有倍数
		    if(i%prime[j]==0) break;//当取模为0的时候,则表示已经为较小的质数,则break
	    }
    }
}

4.排序

        此处提供两个高效排序法,但是一般排序用sort就行。

        快速排序

                平均时间复杂度O(n\log n),最坏情况下为O(n^{^{_{2}}})

void quick_sort(int *a,int left,int right)
{
	int key,l=left,r=right;
	key=a[(l+r)/2];
	while(l<=r)
	{
		while(a[l]<key)l++;
		while(a[r]>key)r--;
		if(l<=r)
		{
			swap(a[l],a[r]);
			l++;r--; 
		}
	}
	if(left<r)  quick_sort(a,left,r);
	if(right>l) quick_sort(a,l,right);
}

        归并排序

                时间复杂度均为O(n\log n)。

void msort(int *a,int *b,int l,int r){
    if(l>=r) return;
    int mid=(l+r)>>1;
    msort(a,b,l,mid);
    msort(a,b,mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid&&j<=r){
        if(a[i]>a[j]) b[k++]=a[j++];
        else b[k++]=a[i++];
    }
    while(i<=mid) b[k++]=a[i++];
    while(j<=r) b[k++]=a[j++];
    for(int i=l;i<=r;i++) a[i]=b[i];
}

5.快速幂

        快速幂板子提供两种版本。

        常规运算版

long long Mod=1e9+7;
long long fastPower(long long base, long long power) {
    long long result = 1;
    while (power > 0) {
        if (power % 2 == 1) {
            result = result * base % Mod;
        }
        power = power / 2;
        base = (base * base) % Mod;
    }
    return result;
}

        位运算版

long long fastPower(long long base, long long power) {
    long long result = 1;
    while (power > 0) {
        if (power & 1) {//此处等价于if(power%2==1)
            result = result * base % Mod;
        }
        power >>= 1;//此处等价于power=power/2
        base = (base * base) % Mod;
    }
    return result;
}

6.背包问题动态规划

        01背包(物品有限个)

for(int i=1;i<=n;i++){
	for(int j=m;j>=w[i];j--){
		dp[j]=max(dp[j],dp[j-w[i]]+v[i]);//状态转移方程
	}
}

        完全背包(物品无限个)

for(int i=1;i<=m;i++){
        for(int j=w[i];j<=t;j++){//此时则为正序遍历
            dp[j]=max(dp[j],(dp[j-w[i]]+v[i]));
        }
    }

7.搜索

        本人做过搜索的题目暂时不多,而且还喜欢用DFS(深度优先搜索),BFS(广度优先搜索)尚不精通,因而这里仅提供两种经典题型:迷宫寻路&查找连通块

        迷宫寻路(回溯法+DFS)

//模板题:洛谷B3625 迷宫寻路

int a[110][110],v[110][110];//a数组记录地图,v数组记录是否走过
int n,m;

//dx,dy两个数组用于表示走的方向
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};
int flag=0;//flag用于标记可以到达


void dfs(int x,int y){
        if(x<1||y<1||x>n||y>m||v[x][y]==1||a[x][y]==1) return;//防止越界、走重复、走到障碍物上

        //找到就返回
        if(x==n&&y==m){
            flag=1;return;
        }
        //走过就标记
        v[x][y]=1;
        //四个方向试探
        for(int i=0;i<=3;i++){
            int tx=x+dx[i];
            int ty=y+dy[i];
            dfs(tx,ty);
        }
}
void solve(){
	cin>>n>>m;
	char x;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>x;
			if(x=='#') a[i][j]=1;
		}
	}
    //从起点开始
	dfs(1,1);

    //判断是否能到达
	if(flag==1) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
}

        查找连通块(DFS)

//洛谷P1596 Lake Counting S

//定义8个方向
int dx[8]={1,0,1,0,-1,-1,1,-1};
int dy[8]={1,1,0,-1,0,-1,-1,1};
int ans=0;

//遇到联通的就试探
void dfs(int x,int y){
	v[x][y]=1;
	int tx,ty;
	for(int i=0;i<=7;i++){
		tx=x+dx[i];
		ty=y+dy[i];
		if(x>n||y>m||x<1||y<1){
			continue;//越界就跳过
		}
		else if(a[tx][ty]==1&&v[tx][ty]==0){
			dfs(tx,ty);
		}
	}
}
void solve(){
	cin>>n>>m;
	char x;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>x;
			if(x=='W') a[i][j]=1;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(a[i][j]==1&&v[i][j]==0){
				dfs(i,j);//开始递归
				ans++;//每递归完成一次表明有一个连通块
			}
		}
	}
	cout<<ans<<endl;
}

8.并查集

        本人尚未精通并查集,故仅提供两个基本操作。并查集原理则自行了解。

        使用并查集之前,记得初始化祖宗节点数组:

int fa[N];//父节点数组,记录每个点的父节点

//父节点的初始化,初始状态全部为其本身(尚未合并)
void begin(int n){
	for(int i=1;i<=n;i++){
		fa[i]=i;
	}
}

          Find

int find(int i){
	if(i!=fa[i]) fa[i]=find(fa[i]);//路径压缩
    return fa[i];
}

        Union

void Union(int a,int b){
	fa[find(a)]=find(b);
}

三、计算策略

1.高精度

        高精度加减法:模拟竖式

lc=(la>lb)?la:lb;
for(i=0;i<=lc;i++){
	c[i]=a[i]+b[i]+n;
	n=c[i]/10;
	c[i]%=10;
}

        高精度乘除:分步取模

        大数相乘或者相除记得有个公式:(a * b) % p = (a % p * b % p) % p

//例:判断是否能被能被603整除
    for(int i=0;i<n;i++){
        mod=(mod*10+(s[i]-'0'))%603;
    }  
    if(mod==0){
        cout<<"YES"<<endl;
    }else{
        cout<<"NO"<<endl;
    }

2.十进制数转二进制数

int f(int x){
    if(x>1) return f(x/2)*10+x%2;
    else return x%2;
}

3.求对数

        cmath库中有专门的函数可以求对数

log() //以e为底的对数
log10() //以10为底的对数

        但是要表示如以3为底的对数呢?答案是换底公式!

double log3(double x){
	return log(x)/log(3);
}

四、STL容器

        CSDN有其他大佬发的更详细更全面的帖子,此处仅供参考Orz......

1.Srting

          若不使用万能头,使用前需加入头文件:

#include<string>

        一个近似替代字符数组的容器。

//定义string,并输入
string s; cin>>s;
//string s[10]相当于二维字符串数组

//获取长度
int len=s.length();
int len=s.size();

//遍历(相当于遍历字符串数组)
for(int i=0;i<len;i++){
    s[i]='1';
}

//比较:可以直接用大于小于号,比较字典序(没有前导零的情况下可用于高精数比较)

//添加字符or添加字符串可以直接用加号

//字符or字符串查找
string str;
s.find(str);//s为被找字符串,str为目标字符串,找不到返回-1!
s.rfind(str);//从末尾开始找

//按字典序排序
sort(s.begin(),s.end());

//字符串去重
unique(s.begin(),s.end());//unique函数用于将字符串中重复的元素自动放到字符串末尾,返回不重复子段的最后一个下标
s.erase(unique(s.begin(),s.end()),s.end());//加上erase函数才能真正实现去重的目的

//字符串截取
s.substr(1);//返回从1开始到字符串末尾的子串
s.substr(1,10);//返回从1开始到10的子串

//返回字符串首地址(用printf输出的时候)
printf("%s",s.c_str());

2.Vector

          若不使用万能头,使用前需加入头文件:

#include<vector>

        本人并不喜欢用vector,但是因人而异,可以将其视为可动态添加元素的数组。

//定义vector(尖括号内为数据类型)
vector<int> a;//a相当于数组名
vector<int> b(10);//定义一个长度为10的ve
vector<int> c(10,1);//定义一个长度为10的ve,初始化为1
vector<int> d[10];//相当于二维vector数组,或者为10个vector


//数据输入
int x;cin>>x;
a.push_back(x);

//常用函数
a.back();//返回最后一个数
a.front();//返回第一个数
a.clear();//清空
a.empty();//判断是否为空
a.size();//返回容器内元素个数
a.erase(a.begin()+1,a.begin()+3);//删除函数,只写一个参数即为删除那个下标的元素,写两个则删除一段区间的元素

//排序
sort(a.begin(),a.end());

//查找元素,返回下标
find(a.begin(),a.end(),10);

//vector的遍历
//法1:类似数组直接下标法
for(int i=0;i<a.size();i++){}
//法2:迭代器(类似于c语言里的指针)
for(vector<int>::iterator i=a.begin();i!=a.end();i++){}
//法3:auto关键字
for(auto i=a.begin();i!=a.end();i++){}
for(auto x:a){}

//vector支持比较运算,按字典序排

3.Stack

          若不使用万能头,使用前需加入头文件:

#include<stack>

        数据结构——栈,是一种先进后出的数据结构。

//栈的定义
stack<int> a;

//常用函数
a.empty(); //判断堆栈是否为空
a.pop(); //弹出堆栈顶部的元素
a.push(); //向堆栈顶部添加元素
a.size(); //返回堆栈中元素的个数
a.top(); //返回堆栈顶部的元素 

4.Queue

        若不使用万能头,使用前需加入头文件:

#include<queue>

        数据结构——队列,是一种先进先出的数据结构。

//队列是一种先进先出的数据结构
//队列的定义
queue<int> a;

//常用函数
a.push(x); //在队尾插入一个元素
a.pop(); //删除队列第一个元素
a.size(); //返回队列中元素个数
a.empty(); //如果队列空则返回true
a.front(); //返回队列中的第一个元素
a.back(); //返回队列中最后一个元素


//拓展:优先队列:
priority_queue<int> pq;//默认大根堆,本身具有顺序,即优先级高的元素先弹出
priority_queue<int,vector<int>,greater<int>> pqs;//定义小根堆优先队列

pq.top(); //访问队头元素
pq.empty(); //队列是否为空
pq.size(); //返回队列内元素个数
pq.push(x); //插入元素到队尾 (并执行上浮操作 )
pq.pop(); //弹出队头元素

5.Map

         若不使用万能头,使用前需加入头文件:

#include<map>

        map也称映射表,对于每一个元素提供一对一的hash。

//map的定义
map<int,int> mp;//尖括号内第一个为键(key)的类型,第二个为值(value)的类型

//常用函数
mp.empty();//返回是否为空
mp.clear();//清空map
mp.erase(x);//删除元素

pair<int,int> xx;
mp.insert(xx);//map的插入一般是插入一个pair

/*
mp.first; //访问键
mp.sceond; //访问值
*/

//如数组一般直接使用map,例如添加映射关系
mp[x]=1;

//遍历map
for(auto it=mp.begin();it!=mp.end();it++){}
for(auto it:mp){}

6.Set&Multiset

        set就是集合,集合中的每个元素只出现一次,并且是排好序的(默认按键值升序排列),而multiset则可以使元素重复出现。

//set的定义(multiset定义同理)
set<int> st;//默认升序序列
set<int,greater<int>> p;//降序序列

//set的常用函数
st.begin(); //返回第一个迭代器
st.end(); //返回末尾的迭代器
st.insert(x); //向集合插入一个元素
st.size(); //返回集合中元素个数
st.empty(); //如果集合空则返回true
st.clear(); //清空集合
st.find(x); //查找某一个数字
st.count(x); //返回multiset内某一元素个数
/*  对于删除操作
    1.输入一个数x,删除所有x
    2.输入一个迭代器,删除这个迭代器 */
st.erase(x); 

st.lower_bound(x); //返回大于等于x的最小值的迭代器(不存在则返回end())
st.upper_bound(x); //返回大于x的最小值的迭代器(不存在则返回end())

五、数论全靠猜!

        由于本人数学并不好,因此只能记录一下赛时遇见的一些数论。

     1 .GCD与LCM

                最小公倍数由两个数的乘积除以最大公因数而得。

int gcd(int a,int b){
    if(b==0) return a;
    return gcd(b,a%b);
}

//求最小公倍数(lcm)即两数乘积除以最大公因数(gcd)
int lcm(int a,int b){
    return a*b/gcd(a,b);
}

        2.n进制数除以n-1的余数

        本文将持续更新~(最后编辑时间2023.12.19)

  • 33
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值