说明
本篇文章只给出代码模板,以及自己对该模板的理解。如果想看正确的算法思路,可以移步AcWing官网看详情。链接:常用代码模板1——基础算法 - AcWing
如有理解错误,欢迎大家批评指正。
简单算法模板
一、排序
1.1 快速排序模板
void quick_sort(int q[],int l,int r){//q是传入的数组,[l,r]是需要排序的范围if(l>=r) return;int i = l-1,j = r+1,x = q[l+r>>1];while(i<j){do i++;while(q[i]<x);do j--;while(q[j]>x);if(i<j) swap(q[i],q[j]);}quick_sort(q,l,j),quick(q,j+1,r);
}
1.2 归并排序
int tmp[N];//需要开一个临时数组tmp[],N至少跟传入的q[]数组的范围一致
void merge_sort(int q[],int l,int r){if(l>=r) return;int mid = r+l>>1;merge_sort(q,l,mid),merge_sort(q,mid+1,r);int k = 0,i = l,j = mid+1;while(i<=mid&&j<=r){if(q[i]<=q[j]) tmp[k++]=q[i++];else tmp[k++]=q[j++];}while(i<=mid) tmp[k++]=q[i++];while(j<=r) tmp[k++]=q[j++];for(int i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}
二、 二分
2.1 整数二分模板1
bool check(int x){//check函数检查x是否满足某种性质//根据题意完善check()函数
}
//区间被划分为[l,mid],[mid+1,r]
int bsearch1(int l,int r){while(l<r){int mid=l+r>>1;if(check(mid)) r=mid;else l=mid+1;}return l;
}
2.2 整数二分模板2
bool check(int x){//check函数检查x是否满足某种性质//根据题意完善check()函数
}
//区间被划分为[l,mid-1],[mid,r]
int bsearch2(int l,int r){while(l<r){int mid=l+r+1>>1;if(check(mid)) l=mid;else r=mid+1;}return l;
}
浮点数二分模板
bool check(double x){//check函数检查x是否满足某种性质//根据题意完善check()函数
}
double bsearch3(double l,double r){const double eps = 1e-8;//定义精度:如果题目要求精确到小数点后六位小数,eps最好开到1e-8保险while(r-l>eps){double mid = (l+r)/2;if(check(mid)) r = mid;else l = mid;}return l;
}
三、高精度
3.1 高精度大数的存储
string a;cin >> a;//以字符串读入大整数
vector<int> A;//使用vector<int> A从个位往高位存储即逆序存储
for(int i = a.size()-1;i>=0;i--) A.push_back(s[i]-'0');
3.2 A+B模板
//C=A+B,A>=0,B>=0
//C++中&还可用于引用变量,常用于传参和定义别名
//如第6行代码,若写成
//vector<int> add(vector<int A,vector<int> B){
//可能会爆内存并且会严重拖慢程序运行速度
vector<int> add(vector<int> &A,vector<int> &B){//参数为大整数A,B,返回A+B的vectorif(A.size()<B.size()) return add(B,A);vector<int> C;int t = 0;//t表示进位for(int i = 0;i<A.size();i++){t+=A[i];if(i<B.size()) t+=B[i];C.push_back(t%10);t/=10;}if(t) C.push_back(t);//如果最高位还有进位,则放入C中return C;
}
3.3 A-B模板
//C=A-B,A>=0,B>=0
vector<int> sub(vector<int> &A,vector<int> &B){//参数为大整数A,B,返回A-B的vectorvector<int> C;int t = 0;//t表示借位for(int i = 0;i<C.size();i++){t=A[i]-t;if(i<B.size()) t-=B[i];C.push_back((t+10)%10);//(t+10)是为了防止t为负数导致取模后出现负数if(t<0) t=1;//有借位else t=0;//没有借位}while(C.size()>1&&C.back()==0) C.pop_back();//去除前导零return C;
}
3.4 A*b模板
//C=A*b,A>=0,b>=0
vector<int> mul(vector<int> &A,int b){//参数为大整数A,小整数b,返回A*b的vectorvector<int> C;int t = 0;//t表示进位值(区别于A+B的进位,t可能是很大的)for(int i = 0;i<A.size()||t;i++){if(i<A.size()) t+=A[i]*b;C.push_back(t%10);t/=10;}while(C.size()>1&&C.back()==0) C.pop_back();//去除前导零return C;
}
3.5 A/b模板
//A/b==C...r,A>=0,b>=0
vector<int> div(vector<int> &A,int b,int &r){//参数为大整数A,小整数b,r用于带回计算出的余数vector<int> C;r = 0;for(int i = A.size();i>=0;i--){r=r*10+A[i];C.push_back(r/b);r%=b;}reverse(C.begin(),C.end());//反转C,方便去除前导零和输出while(C.size()>1&&C.back()==0) C.pop_back();//去除前导零return C;
}
四、前缀和、差分
4.1 一维前缀和模板
//如果我们要求数组a[]大量的子区间[l,r]的数之和
const int N = 1e6+5;
int a[N],prefix[N];//preifx[]为前缀和数组
void solve(){int n;cin >> n;//有n个数,1<=n<=1000000for(int i = 1;i<=n;i++) cin >> a[i];//下标一定从1开始//计算前缀和for(int i = 1;i<=n;i++) prefix[i]=prefix[i-1]+a[i];int q;cin >> q;//有q次询问while(q--){int l,r;cin >> l >> r;//[l,r]为询问区间//输出子区间[l,r]的数之和cout << prefix[r]-prefix[l-1] << endl;//endl -> '\n'}
}
4.2 二维前缀和模板
//如果我们要求矩阵a[][]大量的子矩阵(a[x1][y1]和a[x2][y2]组成的子矩阵)的数之和
const int N = 1e3+5;
int a[N][N],prefix[N][N];//prefix[][]为前缀和数组
void solve(){int n;cin >> n;//n行n列的矩阵,1<=n<=1000for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) cin >> a[i][j];//下标从1开始//计算前缀和for(int i = 1;i<=n;i++){for(int j = 1;j<=n;j++){prefix[i][j]=prefix[i-1][j]+prefix[i][j-1]+a[i][j]-preifx[i-1][j-1];}}int q;cin >> q;//q次询问while(q--){int x1,y1,x2,y2;cin >> x1 >> y1 >> x2 >> y2;//子矩阵的范围//输出子矩阵数之和cout << prefix[x2][y2]-preifx[x1-1][y2]-prefix[x2][y1-1]+prefix[x1-1][y1-1] << endl;//endl -> '\n'}
}
4.3 一维差分模板
//如果我们要大量统一修改数组a[]的区间[l,r]的值,使该区间的数都加上x,并要我们输出最终修改后的数组
const int N = 1e6+5;
int a[N],diff[N];//diff[]为差分数组
void solve(){int n;cin >> n;//有n个数,1<=n<=1000000for(int i = 1;i<=n;i++) cin >> a[i];//下标从1开始//计算差分数组for(int i = 1;i<=n;i++) diff[i]=a[i]-a[i-1];int q;cin >> q;//q次修改while(q--){int l,r,x;cin >> l >> r >> x;//[l,r]区间内的数都加xdiff[l]+=x,diff[r+1]-=x;}for(int i = 1;i<=n;i++) a[i]=a[i-1]+diff[i];//构造修改后的数组a[]for(int i = 1;i<=n;i++) cout << a[i] << " \n"[i==n];//遍历输出,输出完最后一个数后输出换行
}
4.4 二维差分模板
//如果我们要大量统一修改矩阵a[][]的子矩阵(a[x1][y1]和a[x2][y2]形成的子矩阵),使该区间都加上x,并要我们输出最终修改后的数组
const int N = 1e3+5;
int a[N][N],diff[N][N];//diff[][]为差分数组
void update(int x1,int y1,int x2,int y2,int x){diff[x1][y1]+=x,diff[x2+1][y2+1]+=x,diff[x1][y2+1]-=x,diff[x2+1][y1]-=x;
}
void solve(){int n;cin >> n;//n行n列的矩阵,1<=n<=1000for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) cin >> a[i][j];//下标从1开始//计算差分数组for(int i = 1;i<=n;i++){for(int j = 1;j<=n;j++){update(i,j,i,j,a[i][j]);//计算其实就是每次修改一个1×1的小矩阵}}int q;cin >> q;//q次询问while(q--){int x1,y1,x2,y2;cin >> x1 >> y1 >> x2 >> y2;//子矩阵的范围//实现修改update(x1,y1,x2,y2,x);}//构造回最终修改后的矩阵a[][]for(int i = 1;i<=n;i++){for(int j = 1;j<=n;j++){a[i][j]=a[i-1][j]+a[i][j-1]-a[i-1][j-1]-diff[i][j];}}//遍历输出,输出完一行后换行for(int i = 1;i<=n;i++){for(int j = 1;j<=n;j++) cout << a[i][j] << " \n"[j==n];}
}
五、双指针
5.1 双指针模板
bool check(int i,int j){//具体问题的逻辑
}
void solve(){for(int i = 0,j=0;i<n;i++){while(j<i&&check(i,j)) j++;//具体问题的逻辑}
}
//常见问题分类:
// (1) 对于一个序列,用两个指针维护一段区间
// (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
//上述分类大致都包含快慢指针和对撞指针等例题
六、位运算
情景1
求整数n的第k位二进制数字
情景1模板
int find(int n){return n>>k&1;
}
void solve(){int n;cin >> n;//读入ncout << find(n) << endl;//调用函数并输出结果
}
情景2
求整数n的二进制表示中最低位的1的值
情景2模板
int lowbit(int n){//这个计算就被称为lowbit操作return n&(-n);
}
//如:求6的lowbit,6表示二进制补码为(0……0110)即29个'0'+"110"
//-6的补码为(1……1010)即29个'1'+"010"
//而(0……0110)&(1……1010) --> (0……0010)即结果的二进制为30个'0'+"10",转换为十进制的值为2
void solve(){int n;cin >> n;//读入ncout << lowbit(n) << endl;//调用函数并输出结果
}
七、离散化
7.1 离散化模板
vector<int> alls;//存储所有待离散化的值
//使用vector排序去重(要求会默写)
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
// 二分求出x对应的离散化的值
int find(int x){//找到第一个大于等于x的位置int l=0,r=alls.size()-1;while (l<r){int mid=l+r>>1;if(alls[mid]>=x) r=mid;else l=mid+1;}return r+1;//映射到1,2,...,n
}
八、 区间合并
8.1 区间合并模板
// 将所有存在交集的区间合并
void merge(vector<PII> &segs){//PII->pair<int,int>,pair存储的是左端点l和右端点r的二元组,合并后的结果放回segsvector<PII> res;sort(segs.begin(),segs.end());int st=-2e9,ed=-2e9;for(auto seg:segs){if(ed<seg.first){if(st!=-2e9) res.push_back({st,ed});st=seg.first,ed=seg.second;}else ed=max(ed,seg.second);}if (st!=-2e9) res.push_back({st, ed});segs=res;
}