Acwing基础算法模板

文章目录

  • 第一章 基础算法
    • 排序
      • 快速排序
      • 归并排序
    • 二分
      • 整数二分
      • 浮点数二分
    • 高精度
      • 高精度加法
      • 高精度减法
      • 高精度乘法
      • 高精度除以高精度
    • 前缀和差分
      • 一维前缀
      • 二维前缀和
      • 一维差分
      • 二维差分


第一章 基础算法

AcWing基础算法

代码模板要理解背过
课下把思想搞懂,能默写出来
代码删掉重复写三四遍就差不多

第一节课

排序

快速排序

算法模板:

void quick_sort(int q[], int l, int r) {
	if (l >= r) {
		return;
	}
	int i = l - 1, j = r + 1, x = q[(l + r) / 2];
	while (i < j) {
		do {
			i++;
		} while (q[i] < x);
		do {
			j--;
		} while (q[j] > x);
		if (i < j) {
			//TODO
			swap(q[i], q[j]);     // swap()函数
		}
		quick_sort(q, l, j); // 为什么选择j ?
		quick_sort(q, j + 1, r);
	}


}
//交换两个数
if(i<j){
	//TODO
	int t=q[i];
	q[i]=q[j];
	q[j]=t;
}

例题: 785. 快速排序
Acwing基础算法模板_第1张图片
题解:

#include
using namespace std;
const int N = 1e6 + 10;
int n;
int q[N];
void quick_sort(int q[], int l, int r) {
	if (l >= r) {
		return;
	}
	int i = l - 1, j = r + 1, x = q[(l + r) / 2];
	while (i < j) {
		do {
			i++;
		} while (q[i] < x);
		do {
			j--;
		} while (q[j] > x);
		if (i < j) {
			//TODO
			swap(q[i], q[j]);
		}
		quick_sort(q, l, j);
		quick_sort(q, j + 1, r);
	}

}
int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		//TODO
		scanf("%d", &q[i]);
	}
	quick_sort(q, 0, n - 1);
	for (int i = 0; i < n; i++) {
		//TODO
		printf("%d ", q[i]);
	}
	return 0;
}

归并排序

中间划分,合并,扫尾,还原
算法模板:

void merge_sort(int q[], int l, int r) {
	if (l >=  r) {
		return;
	}
	int mid = l + r >> 1;  // 分界点
	merge_sort(q, l, mid);
	merge_sort(q, mid + 1, r);
	int k = 0, i = l, j = mid + 1;// i是左半边区间的左边界,j是右半边区间的右边界。
	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++];
	}
	// tmp[]数组元素复制存储到原数组q[],还原
	for(i=l,j=0;i<=r;i++,j++){
		q[i]=tmp[j];
	}
	
}

快速排序是稳定的
归并排序是不稳定的
若想要快排稳定,只要让快排数组中每个数都不同,设置一个双元组,把下标也放进去,形成pair()

例题:787. 归并排序
Acwing基础算法模板_第2张图片
题解:

#include
using namespace std;
const int N = 100010;
int n;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r) {
	if (l >=  r) {
		return;
	}
	int mid = l + r >> 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++];
	}
	// tmp[]数组元素复制存储到原数组q[]
	for (i = l, j = 0; i <= r; i++, j++) {
		q[i] = tmp[j];
	}

}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++) {
		//TODO
		scanf("%d", &q[i]);
	}
	merge_sort(q, 0, n - 1);
	for (int i = 0; i < n; i++) {
		//TODO
		printf("%d ", q[i]);
	}
	return 0;
}

二分

整数二分

会有很多边界问题
有单调性的题目一定可以二分
算法模板:

bool check(int x) { // 检查x是否满足某种性质
	
}

//情况一: 区间[l,r]被划分成[l,mid]和[mid+1,r]时使用
int bsearch_1(int l, int r) {
	while (l < r) {
		int mid = (l + r) / 2;  // 第一种不需要补上加一
		if (check(mid)) { // check()判断mid是否满足性质
			//TODO
			r = mid;
		} else {
			l = mid + 1;
		}
	}
	return l;
}
// 情况二:区间[l,r]被划分成[l,mid-1]和[mid,r]时使用:
int bsearch_2(int l, int r) {
	while (l < r) {
		//TODO
		int mid = (l + r + 1) / 2;  // 第二种需要补上加一
		if (check(mid)) {
			l = mid;
			//TODO
		} else {
			r = mid - 1;
		}
	}
	return l;
}

例题:789. 数的范围
Acwing基础算法模板_第3张图片

C++里面的取整是往下取整
一个题目,如果一个区间具有单调性质,那么一定可以二分,但是如果说这道题目没有单调性质,而是具有某种区间性质的话,我们同样可以使用二分.
​ ——yxc总

#include
using namespace std;
const int N = 1000010;
int n, m;
int q[N];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++) {
		scanf("%d", &q[i]);
	}
	while (m--) {
		int x;
		scanf("%d", &x);
		int l = 0, r = n - 1; // 先设置两个左右指针

		while (l < r) {
			int mid = (l + r) / 2; // 区间[l,r]被划分成区间[l,mid]和[mid+1,r]
			if (q[mid] >= x) { // 满足大于等于x的数,从x位置开始所有数都大于等与x
				r = mid;
			} else {
				l = mid + 1;
			}
		} // 循环结束,l=r

		if (q[l] != x) {
			cout << "-1 -1" << endl;
		} else {
			cout << l << " "; // 找到了子序列的左边界
			int l = 0, r = n - 1;
			while (l < r) {
				int mid = (l + r + 1) / 2; // 区间[l,r]被划分成[l,mid-1]和[mid,r]时使用
				if (q[mid] <= x) { //定义x左边所有的数都小于等于x
					l = mid;  //因为是第二个模板所以需要补上加一

				} else {
					r = mid - 1;
				}
			}
			cout << l << endl;
		}
	}
	return 0;
}

浮点数二分

本质上也是一个边界
算法模板:

bool check(double) { // 检查x是否满足某种性质
	
}
double bsearch_3(double l, double r) {
	const double eps = 1e-6; // eps表示精度,取决于题目的要求
	while (r - l > eps) {
		double mid = (l + r) / 2;
		if (check(mid)) {
			r = mid;
		} else {
			l = mid;
		}
	}
	return l;
}

例题:790. 数的三次方根
Acwing基础算法模板_第4张图片

#include
using namespace std;
int main() {
	double x;
	cin >> x;
	// 0.01=0.1*0.1
	double l = -10000, r = 1000;
	while (r - l > 1e-8) {
		double mid = (l + r) / 2 ;

		if (mid * mid * mid < x) {
			l = mid;

		} else {
			r = mid;
		}
	}
	printf("%lf", l);

	return 0;
}

第二节课

高精度

高精度加法

大整数每一位存储到数组里面,高位在前还是低位在前
123456789
第0位应该存1还是存9?
我们第0位存的是个位9,第一位存储十位8

原因:两个整数相加可能会进位,在末尾添加数字很容易,在首位添加数组所有数字往后移,不方便。

模板:


#include
#include
#include
using namespace std;
// C =A+B ,A>=0,B>=0 
vector<int> add(vector<int> &A, vector<int> &B) {
	if (A.size() < B.size()) {
		return add(B, A);  // 使得A向量长度大于B向量的长度
	}
	vector<int> C;
	int t = 0;  
	for (int i = 0; i < A.size(); i++) {
		t = t + A[i];
		if (i < B.size()) {
			t = t + B[i];
		}
		C.push_back(t % 10);
		t = t / 10;  // 整除10是进位的数字,逢十进一
	}
	if (t) {
		C.push_back(t);
	}
	return C;
}

例题:791. 高精度加法
Acwing基础算法模板_第5张图片

#include
#include

using namespace std;
const int N = 1e6 + 10;
// C=A+B
vector<int> add(vector<int> &A, vector<int> &B) {
	// 数组加引用是为了提高效率,如果不加引用,就会把整个数组复制一遍
	vector<int> C;
	int t = 0;
	for (int i = 0; i < A.size() || i < B.size(); i++) {
		//i 是有符号整数,A.size()是无符号整数
		if (i < A.size()) {
			t = t + A[i];
		}
		if (i < B.size()) {
			t = t + B[i];

		}
		C.push_back(t % 10);
		t /= 10;
	}
	if (t) {
		C.push_back(1);
	}
	return C;


}

int main() {
	string a, b; // 太长用字符串先表示
	vector<int>A, B;
	cin >> a >> b; //a='123456'
	for (int i = a.size() - 1; i >= 0; i--) {
		A.push_back(a[i] - '0'); // A=[6,5,4,3,2,1]
	}
	for (int i = b.size() - 1; i >= 0; i--) {
		//TODO
		B.push_back(b[i] - '0'); //  B
	}
	auto C = add(A, B);
	for (int i = C.size() - 1; i >= 0; i--) {
		printf("%d", C[i]);
	}

	return  0;
}

高精度减法

加减存储过程是一样的,因为在计算结果的过程中可能会涉及加减乘除

模板:

// C= A-B 满足(保证较大的数减去较小的数,保证最前面的数不会借位)A>=B, A>=0,B>=0
// 若A sub(vector &A, vector &B) {
	vector C;
	for (int i = 0, t = 0; i < A.size(); i++) {
		t = A[i] - t;
		if (i < B.size()) {
			t -= B[i];
		}
		C.push_back((t + 10) % 10);  // 有两种情况t>=0 就是t 和t<0就t+10  总结为(t + 10) % 10
		if (t < 0) {
			t = 1;
		} else {
			t = 0;
		}
	}
	while (C.size() > 1 && C.push_back() == 0) {
		C.push_back();
	}
	return C;
}

例题:792. 高精度减法
Acwing基础算法模板_第6张图片

#include
#include
#include
using namespace std;
// 判断是否有A>=B
bool cmp(vector<int>&A, 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

}
// C=A-B
vector<int> sub(vector<int> &A, vector<int> &B) {
	vector<int> C;
	for (int i = 0, t = 0; i < A.size(); i++) {
		t = A[i] - t;
		if (i < B.size()) { // 判断B的位数是否存在,因为B
			t -= B[i];
		}
		C.push_back((t + 10) % 10);  // 有两种情况t>=0 就是t 和t<0就t+10  总结为(t + 10) % 10
		if (t < 0) { // 判断是否需要借位,t只有两种状态1和0
			t = 1;
		} else {
			t = 0;
		}
	}
	while (C.size() > 1 && C.back() == 0) { // 去掉前导0
		C.pop_back();
	}
	return C;
}
int main() {
	string a, b; // 太长用字符串先表示
	vector<int>A, B;
	cin >> a >> b; //a='123456'
	for (int i = a.size() - 1; i >= 0; i--) {
		A.push_back(a[i] - '0'); // A=[6,5,4,3,2,1]
	}
	for (int i = b.size() - 1; i >= 0; i--) {
		//TODO
		B.push_back(b[i] - '0'); //  B
	}
	if (cmp(A, B)) {
		auto C = sub(A, B);
		for (int i = C.size() - 1; i >= 0; i--) {
			printf("%d", C[i]);
		}

	} else {
		auto C = sub(B, A);
		for (int i = C.size() - 1; i >= 0; i--) {
			printf("%d", C[i]);
		}
	}
	return  0;
}

高精度乘法

模板:

// C=A*b ,A>=0,b>=0 ,把B看作一个整体和A的每个数字相乘,不像普通的乘法
vector<int> mul(vector<int>&A, int b) {
	vector C;
	int t = 0;

	for (int i = 0; i <= A.size() || t; i++) {
		//TODO
		if (i < A.math()) {
			t += A[i] * b;
			//TODO
			C.push_back(t % 10);
			t /= 10;
		}
	}
	while (C.size() > 1 && C.back() == 0) {
		C.pop_back();
	}

	return C;
}

例题:793. 高精度乘法
Acwing基础算法模板_第7张图片


#include
#include
using namespace std;
vector<int> A;
vector<int>mul(vector<int>&A, int b) {
	vector<int> C;
	int t = 0; // 进位
	for (int i = 0; i < A.size() || t; i++) {
		if (i < A.size()) {
			t += A[i] * b;
		}
		C.push_back(t % 10);
		t /= 10;
	}
	return C;
}
int main() {
	string a; // a很长用字符串来存储
	int b; // b很短用数组来存储
	cin >> a >> b;
	for (int i = a.size() - 1; i >= 0; i--) {
		A.push_back(a[i] - '0');
	}
	auto C = mul(A, b);
	for (int i = C.size() - 1; i >= 0; i--) {
		printf("%d", C[i]);
	}

	return  0;
}

高精度除以高精度

模板:

//  A/b =C ...r ,A>=0,b>0
vector<int> div(vector<int>&A, int b, int &r) {
	vector<int> C;
	r = 0;
	for (int i = A.size() - 1; i >= 0; i--) {
		r = r * 10 + A[i];
		C.push_back(r / b);
		r %= b;
	}
	reverse(C.begin(), C.end());
	while (C.size() > 1 && C.back() == 0) {
		C.pop_back();
		//TODO
	}
	return C;
}

例题:794. 高精度除法
Acwing基础算法模板_第8张图片

#include
#include
using namespace std;
// A/b ,商是C ,余数是r
vector<int> div(vector <int>&A, int b, int &r) {
	vector<int> C;  // 商
	r = 0;
	for (int i = A.size() - 1; i >= 0; i--) { // 从最高位开始
		r = r * 10 + A[i];
		C.push_back(r / b);
		r %= b;
	}
	reverse(C.begin(), C.end());
	while (C.size() > 1 && C.back() == 0) { // 去掉前导0
		C.pop_back();
	}
	return C;

}
int main() {
	string a;
	int b;
	cin >> a >> b;
	vector<int>A;
	for (int i = a.size() - 1; i >= 0; i--) {
		A.push_back(a[i] - '0');
	}
	int r;
	auto C = div(A, b, r);
	for (int i = C.size() - 1; i >= 0; i--) {
		printf("%d", C[i]);
	}
	cout << endl << r << endl;
	return 0;

}

前缀和差分

一维前缀

作用:快速求出来数组中一段数组元素的和
模板:

公式:
S[i] =a[1]+a[2]+...+a[i];
a[l]+...+a[r]=S[r]-S[l-1]
s[r]=a[1]+a[2]+...+a[l-1]+a[l]+...+a[r]
s[l-1]=a[1]+a[2]+...a[l-1]

把s[0]定义为0,统一处理所有情况

例题:795. 前缀和
Acwing基础算法模板_第9张图片
题解:

#include
using namespace std;
const int N = 100010;
int n, m;
int a[N], s[N];
int main() {
	scanf("%d%d", &n, &m);
	for (int i = 0; i < n; i++) {
		scanf("%d", &a[i]); //TODO
	}
	for (int i = 0; i < n; i++) {
		s[i] = s[i - 1] + a[i]; //TODO
	}
	while (m--) {
		int l, r;
		scanf("%d%d", &l, &r); //TODO
		printf("%d\n", s[r] - s[l - 1]);
	}
	return 0;
}

二维前缀和

模板:
Acwing基础算法模板_第10张图片

S[i,j] =第i行j列格子上左上部分所有元素的和
以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵的和:
S[x2,y2]-S[x1-1,y2]-S[x2,y1-1]+S[x1-1,y1-1]

例题:796. 子矩阵的和
Acwing基础算法模板_第11张图片

#include

using namespace std;
const int N = 10010;
int n, m, q;
int a[N][N], s[N][N];
int main() {
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%d", &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]-s[i-1][j-1]+a[i][j]; // 求前缀和
		}
	}
	while(q--){
		int x1,y1,x2,y2;
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		printf("%d\n",  s[x2][y2] - s[x2][y1 - 1] - s[x1 - 1][y2] + s[x1 - 1][y1 - 1]); 
	}
	
	return 0;
}

一维差分

Acwing基础算法模板_第12张图片
模板:

给区间[l,r]中的每一个数加上c
B[i]+=c
B[r+1]-=c

例题:797. 差分
Acwing基础算法模板_第13张图片

题解:

// 使用差分,时间复杂度为O(1)
#include
using namespace std;
const int N=100010;
int a[N],b[N];
int n,m;

int main () {
	scanf("%d%d",&n,&m);
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
		b[i]=a[i]-a[i-1];
	}

	int l,r,c;
	while(m--) {

		scanf("%d%d%d",&l,&r,&c);
		b[l]+=c;  // 从l开始所有元素差分数组都加上c
   		 b[r+1]-=c;  // 从r往后的元素已经加上了c,所以要减去c
	}
	for(int i=1; i<=n; i++) {
		a[i]=b[i]+a[i-1]; // 前缀和运算
		printf("%d ",a[i]);
	}
	return 0;
}

二维差分

模板:

给以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中的所有元素加上c
S[x1,y1]+=c
S[x2,y1]-=c
S[x1,y2+1]-=c
S[x2+1,y2+1]+=c

798: 差分矩阵

Acwing基础算法模板_第14张图片题解:

// 差分数组中没有前缀和
#include
#include
using namespace std;
const int N = 1010;
int n, m, q;
int a[N][N], b[N][N];
void insert(int x1, int y1, int x2, int y2, int c) {
	b[x1][y1] += c;  //右下角部分的矩形全部加上c, x1,y1,x2,y2递增
	b[x1][y2 + 1] -= c;
	b[x2 + 1][y1] -= c;
	b[x2 + 1][y2 + 1] += c;

}
int main() {
	scanf("%d%d%d", &n, &m, &q);
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			scanf("%d", &a[i][j]);
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			insert(i, j, i, j, a[i][j]); // 构建差分数
		}
	}
	while (q--) {
		int x1, y1, x2, y2, c;
		scanf("%d%d%d%d%d", &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++) {
			b[i][j] += b[i - 1][j] + b[i][j - 1] - b[i - 1][j - 1]; // 二位前缀和
		}
	}
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {
			printf("%d ", b[i][j]);
		}
		printf("\n");
	}

	return 0;
}

例题:796. 子矩阵的和
Acwing基础算法模板_第15张图片题解:

#include
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],s[N][N];
int main(){
  scanf("%d%d%d",&n,&m,&q);
  for(int i=1;i<=n;i++){
    for(int j=1;j<=m;j++){
      scanf("%d",&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]-s[i-1][j-1]+a[i][j];// 初始化前缀和数组
    }
  }
  // 询问
  while(q--){
    int x1,y1,x2,y2;
    scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
    printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
  }
  
  
  return 0;
}

你可能感兴趣的:(算法,算法,排序算法,数据结构)