说明
本篇文章只给出代码模板,以及自己对该模板的理解。如果想看正确的算法思路,可以移步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的vector
if(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的vector
vector<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的vector
vector<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<=1000000
for(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<=1000
for(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<=1000000
for(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]区间内的数都加x
diff[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<=1000
for(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;//读入n
cout << 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;//读入n
cout << 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的二元组,合并后的结果放回segs
vector<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;
}