第一讲 基础算法
1.1快速排序
快速排序算法模板 —— 模板题 AcWing 785. 快速排序
void quick_sort(int q[], int l, int 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_sort(q, j + 1, r); }
1.1.1 785. 快速排序
给定你一个长度为 n 的整数数列。
请你使用快速排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整个数列。
输出格式
输出共一行,包含 n 个整数,表示排好序的数列。
数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
代码:
#include<bits/stdc++.h> using namespace std; const int N=100010; int A[N]; int n; int main() { cin>>n; for(int i=0;i<n;i++) { cin>>A[i]; } sort(A,A+n); for(int i=0;i<n;i++) { cout<<A[i]<<" "; } return 0; }
1.1.2 786. 第k个数
给定一个长度为 n 的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。
输入格式
第一行包含两个整数 n 和 k。
第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第 k 小数。
数据范围
1≤n≤100000,
1≤k≤n
输入样例:
5 3
2 4 1 5 3
输出样例:
3
代码:
#include<bits/stdc++.h> using namespace std; const int N=100010; int n,k; int A[N]; int main() { cin>>n>>k; for(int i=0;i<n;i++) { cin>>A[i]; } sort(A,A+n); for(int i=0;i<n;i++) { if(i+1==k) { cout<<A[i]; break; } } return 0; }
1.2归并排序
归并排序算法模板 —— 模板题 AcWing 787. 归并排序
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 ++ ]; for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j]; }
1.2.1 787. 归并排序
给定你一个长度为 n 的整数数列。
请你使用归并排序对这个数列按照从小到大进行排序。
并将排好序的数列按顺序输出。
输入格式
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在 1∼109 范围内),表示整个数列。
输出格式
输出共一行,包含 n 个整数,表示排好序的数列。
数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
代码:
#include<bits/stdc++.h> using namespace std; const int N=100010; int n; int A[N]; int main() { cin>>n; for(int i=0;i<n;i++) { cin>>A[i]; } sort(A,A+n); for(int i=0;i<n;i++) { cout<<A[i]<<" "; } return 0; }
1.2.2 788. 逆序对的数量
给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。
逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 ia[j],则其为一个逆序对;否则不是。
输入格式
第一行包含整数 n,表示数列的长度。
第二行包含 n 个整数,表示整个数列。
输出格式
输出一个整数,表示逆序对的个数。
数据范围
1≤n≤100000
输入样例:
6
2 3 4 5 6 1
输出样例:
5
代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N=100010; int n; LL ans=0; int A[N],temp[N]; LL merge_sort(int q[],int l,int r) { if(l>=r) { return 0; } int mid=(l+r)/2; LL res=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]) { temp[k++]=q[i++]; } else { res+=mid-i+1; temp[k++]=q[j++]; } } while(i<=mid) { temp[k++]=q[i++]; } while(j<=r) { temp[k++]=q[j++]; } for(i=l,j=0;i<=r;i++,j++) { q[i]=temp[j]; } return res; } int main() { cin>>n; for(int i=0;i<n;i++) { cin>>A[i]; } cout<<merge_sort(A,0,n-1); return 0; }
1.3二分
整数二分算法模板 —— 模板题 AcWing 789. 数的范围
bool check(int x) {/* … */} // 检查x是否满足某种性质
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用: int bsearch_1(int l, int r) { while (l < r) { int mid = l + r >> 1; if (check(mid)) r = mid; // check()判断mid是否满足性质 else l = mid + 1; } return l; } // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用: int bsearch_2(int l, int r) { while (l < r) { int mid = l + r + 1 >> 1; if (check(mid)) l = mid; else r = mid - 1; } return l; }
浮点数二分算法模板 —— 模板题 AcWing 790. 数的三次方根
bool check(double x) {/* … */} // 检查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; }
1.3.1 789. 数的范围
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1∼10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围
1≤n≤100000
1≤q≤10000
1≤k≤10000
输入样例:
6 3
1 2 2 3 3 4
3
4
5
输出样例:
3 4
5 5
-1 -1
#include<bits/stdc++.h> using namespace std; const int N=100010; int n,q; int A[N],st[N],ed[N]; bool K[N]={false}; map<int,pair<int,int> >mp; int main() { cin>>n>>q; for(int i=0;i<n;i++) { cin>>A[i]; K[A[i]]=true; if(i==0) { st[i]=0; } else { if(A[i]==A[i-1]) { st[i]=st[i-1]; } else { st[i]=i; } } } for(int i=n-1;i>=0;i--) { if(i==n-1) { ed[i]=i; } else { if(A[i]==A[i+1]) { ed[i]=ed[i+1]; } else { ed[i]=i; } } } for(int i=0;i<n;i++) { mp[A[i]]=make_pair(st[i],ed[i]); } while(q--) { int k; cin>>k; if(K[k]==false) { cout<<"-1 -1"<<endl; } else { cout<<mp[k].first<<" "<<mp[k].second<<endl; } } return 0; }
1.3.2 790. 数的三次方根
给定一个浮点数 n,求它的三次方根。
输入格式
共一行,包含一个浮点数 n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。
数据范围
−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
#include<bits/stdc++.h> using namespace std; const double N=10000,eps=1e-8; double n; bool check(double mid) { mid=mid*mid*mid; if(n>mid) { return true; } else { return false; } } double seg(double l,double r) { while(r-l>eps) { double mid=(l+r)/2; if(check(mid)) { l=mid; } else { r=mid; } } return l; } int main() { cin>>n; printf("%.6f",seg(-N,N)); return 0; }
1.4高精度
高精度加法 —— 模板题 AcWing 791. 高精度加法
// C = A + B, A >= 0, B >= 0
// 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); vector<int> C; int t = 0; 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); return C; }
高精度减法 —— 模板题 AcWing 792. 高精度减法
// C = A - B, 满足A >= B, A >= 0, B >= 0 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()) t -= B[i]; C.push_back((t + 10) % 10); if (t < 0) t = 1; else t = 0; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
高精度乘低精度 —— 模板题 AcWing 793. 高精度乘法
// C = A * b, A >= 0, b >= 0 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; } while (C.size() > 1 && C.back() == 0) C.pop_back(); return C; }
高精度除以低精度 —— 模板题 AcWing 794. 高精度除法
// 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(); return C; }
1.4.1 791. 高精度加法
给定两个正整数,计算它们的和。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的和。
数据范围
1≤整数长度≤100000
输入样例:
12
23
输出样例:
35
#include<bits/stdc++.h> using namespace std; const int N=100010; struct bign{ int d[N]; int len; bign() { memset(d,0,sizeof(d)); len=0; } }; bign change(string str) { bign a; a.len=str.length(); for(int i=0;i<str.length();i++) { a.d[i]=str[a.len-i-1]-'0'; } return a; } bign add(bign a,bign b) { bign c; int carry=0; for(int i=0;i<a.len||i<b.len;i++) { int temp=a.d[i]+b.d[i]+carry; c.d[c.len++]=temp%10; carry=temp/10; } if(carry!=0) { c.d[c.len++]=carry; } return c; } void print(bign a) { for(int i=a.len-1;i>=0;i--) { cout<<a.d[i]; } } int main() { bign a,b; string sa,sb; cin>>sa>>sb; a=change(sa),b=change(sb); print(add(a,b)); return 0; }
1.4.2 792. 高精度减法
给定两个正整数,计算它们的差,计算结果可能为负数。
输入格式
共两行,每行包含一个整数。
输出格式
共一行,包含所求的差。
数据范围
1≤整数长度≤105
输入样例:
32
11
输出样例:
21
#include<bits/stdc++.h> using namespace std; const int N=100010; struct bign{ int d[N]; int len; bign() { memset(d,0,sizeof(d)); len=0; } }; bign change(string str) { bign a; a.len=str.length(); for(int i=0;i<str.length();i++) { a.d[i]=str[a.len-i-1]-'0'; } return a; } int compare(bign a,bign b) { if(a.len>b.len) { return 1; } else if(a.len<b.len) { return -1; } else { for(int i=a.len-1;i>=0;i--) { if(a.d[i]>b.d[i]) { return 1; } else { if(a.d[i]<b.d[i]) { return -1; } } } return 0; } } bign sub(bign a,bign b) { bign c; int carry=0; for(int i=0;i<a.len||i<b.len;i++) { if(a.d[i]<b.d[i]) { a.d[i+1]--; a.d[i]+=10; } c.d[c.len++]=a.d[i]-b.d[i]; } while(c.len-1>=1&&c.d[c.len-1]==0) { c.len--; } return c; } void print(bign a) { for(int i=a.len-1;i>=0;i--) { cout<<a.d[i]; } } int main() { bign a,b; string sa,sb; cin>>sa>>sb; a=change(sa),b=change(sb); if(compare(a,b)>0) { print(sub(a,b)); } else if(compare(a,b)<0) { cout<<"-"; print(sub(b,a)); } else { cout<<0; } return 0; }
1.4.3 793. 高精度乘法
给定两个正整数 A 和 B,请你计算 A×B 的值。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共一行,包含 A×B 的值。
数据范围
1≤A的长度≤100000,
0≤B≤10000
输入样例:
2
3
输出样例:
6
#include<bits/stdc++.h> using namespace std; const int N=100010; struct bign{ int d[N]; int len; bign() { memset(d,0,sizeof(d)); len=0; } }; bign change(string str) { bign a; a.len=str.length(); for(int i=0;i<str.length();i++) { a.d[i]=str[a.len-i-1]-'0'; } return a; } bign multi(bign a,int b) { bign c; int carry=0; for(int i=0;i<a.len;i++) { int temp=a.d[i]*b+carry; c.d[c.len++]=temp%10; carry=temp/10; } while(carry!=0) { c.d[c.len++]=carry%10; carry/=10; } return c; } void print(bign a) { for(int i=a.len-1;i>=0;i--) { cout<<a.d[i]; } } int main() { bign a; int b; string sa; cin>>sa>>b; a=change(sa); if(b==0) { cout<<0; } else { print(multi(a,b)); } return 0; }
1.4.4 794. 高精度除法
给定两个非负整数 A,B,请你计算 A/B 的商和余数。
输入格式
共两行,第一行包含整数 A,第二行包含整数 B。
输出格式
共两行,第一行输出所求的商,第二行输出所求余数。
数据范围
1≤A的长度≤100000,
1≤B≤10000,
B 一定不为 0
输入样例:
7
2
输出样例:
3
1
#include<bits/stdc++.h> using namespace std; const int N=100010; struct bign{ int d[N]; int len; bign() { memset(d,0,sizeof(d)); len=0; } }; bign change(string str) { bign a; a.len=str.length(); for(int i=0;i<str.length();i++) { a.d[i]=str[a.len-i-1]-'0'; } return a; } bign divide(bign a,int b,int &r) { bign c; c.len=a.len; for(int i=a.len-1;i>=0;i--) { r=r*10+a.d[i]; if(r<b) { c.d[i]=0; } else { c.d[i]=r/b; r=r%b; } } while(c.len-1>=1&&c.d[c.len-1]==0) { c.len--; } return c; } void print(bign a) { for(int i=a.len-1;i>=0;i--) { cout<<a.d[i]; } } int main() { bign a; int b; string sa; cin>>sa>>b; a=change(sa); int r; print(divide(a,b,r)); cout<<endl<<r; return 0; }