基础算法(完结)
二分法:
对于某些问题,如果我们从正面直接求解非常困难或者根本不可能,但如果这个问题的解空间是有限的,那么我们就可以判断解空间中的每一个可能的答案来寻找这个问题的解。我们可以选择线性地遍历整个解空间,但是如果数据足够多,那么即便是计算机也未必可以很快的求出解,那么通过我们人为的观察,如果这组数据满足一个条件——答案的左侧数据都不满足check,而右侧的数据都满足check,或者反之,那么我们就可以利用二分法来找到这个答案。它的作用是每判断一次都能使解空间的大小缩小一半。
整数二分(一)return解空间中第一次出现满足条件的解
int binary(int l, int r) {
while(l < r) {
int mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
整数二分(二)return解空间中最后一次出现满足条件的解
int binary(int l, int r) {
while(l < r) {
int mid = l + r + 1 >> 1;
if(check(mid)) l = mid;
else r = mid - 1;
}
return l;
}
浮点数二分
double binary(double l, double r) {
const double eps = 1e-6 // 表示精度
while(r - l > eps) {
double mid = (l + r) / 2;
if(check(mid)) r = mid;
else l = mid;
}
return l;
}
同时,如果是单纯地在数组中二分查找一个数的话,C++STL提供了对应的函数
-
返回第一个不小于val的迭代器
lower_bound(s.begin(),s.end(),val);
-
返回第一个大于val的迭代器
upper_bound(s.begin(),s.end(),val);
习题链接:
P1873 [COCI 2011/2012 #5] EKO / 砍树 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
https://codeforces.com/problemset/problem/1574/C
高精度
long long 最大能表示的数是9223372036854775807,这个数不小,但是在C++中,如果想要表示比这个数还大的数就要使用高精度数组来表示这个数。
高精度加法
//得到个位在第一位的数组
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() || t; i ++) {
if(i < A.size() ) t += A[i];
if(i < B.size() ) t += B[i];
C.push_back(t % 10);
t /= 10;
}
return C;
}
高精度减法
//要求A >= B >= 0
vector<int> sub(vector<int> &A, vector<int> &B) {
vector<int> C;
int t = 0;//表示借位
for(int i = 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;
}
高精度乘法(高精度乘低精度)
//要求 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;
}
高精度乘法(高精度乘高精度)
vector<int> mul(vector<int> &A, vector<int> &B){
vector<int> C(A.size() + B.size() );
for(int i = 0,t = 0; i < A.size(); i++) {
for(int j = 0; j < B.size() || t; j ++) {
C[i + j] += A[i] * B[j] + t;
t = C[i + j] / 10;
C[i + j] %= 10;
}
C[i + B.size()] += t;
}
while(C.back() == 0 && C.size() > 1) C.pop_back();
return C;
}
高精度除法(高精度除低精度)
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;
}
高等排序
对于初等排序冒泡排序、选择排序、拆入排序,虽然能够完成目的,但是效率其实非常底下,由此便诞生出了基于分治思想的快速排序和归并排序。
快速排序
void quick_sort(vector<int> &a, int l, int r) {
if(l >= r) return;
int x = a[l + r >> 1], i = l - 1, j = r + 1;
while(i < j) {
do(i ++); while(a[i] < x);
do(j --); while(a[j] > x);
if(i < j) swap(a[i], a[j]);
}
quick_sort(a, l, j);
quick_sort(a, j + 1, r);
}
归并排序
void merge_sort(vector<int> &a, int l, int r) {
vector<int> tmp;
if(l >= r) return ;
int mid = l + r >> 1;
int i = l, j = mid + 1;
merge_sort(a, l, mid), merge_sort(a,mid + 1, r);
while(i <= mid && j <= r) {
if(a[i] < a[j]) tmp.push_back(a[i ++]);
else tmp.push_back(a[j ++]);
}
while(i <= mid) tmp.push_back(a[i ++]);
while(j <= r) tmp.push_back(a[j ++]);
for(int i = 0, j = l; i < tmp.size(); i ++) {
a[j ++] = tmp[i];
}
}
字符串KMP
用来判断一个模板串是否是一个主串的字串
bool KMP(string s, string t) {
int n = s.size(), m = t.size();
vecotr<int> ne(m);
for(int i = 1, k = 0; i < m; i ++) {
while(k > 0 && t[i] != t[k]) {
k = ne[k - 1];
}
if(t[i] == t[k]) {
k ++;
}
ne[i] = k;
}
for(int i = 0, j = 0; i < n; i ++) {
while(j > 0 && s[i] != t[j]) {
j = ne[j - 1];
}
if(s[i] == t[j]) {
i ++, j ++;
}
if(j == m) {
return 1;
}
}
return 0;
}
数学
快速幂
ll qpow(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res *= a;
b >>= 1;
a *= a;
}
return res;
}
快速乘
//return (a * b) % m;
//利用unsigned long long的自然溢出
//时间复杂度O(1)
ll binmul(ll a, ll b, ll m) {
ull c = (ull)a * b - (ull)((long double)a / m * b + 0.5L) * m;
return (c + m) % m;
}
进制转换(秦九韶算法)
int solve(string s, int k) {
int res = 0;
for(int i = 0; i < s.size(); i ++) {
res = res * k + s[i] - '0';
}
return res;
}
区间合并
顾名思义,就是将几段存在交集的区间合并成几段没有交际的区间
vector<PII> merge(vector<PII> segs) {
vector<PII> res;
sort(segs.begin(), segs.end());
int st = segs[0].first, ed = segs[0].second;
for(auto x : segs) {
if(x.first <= ed) {
ed = max(x.second, ed);
} else {
res.push_back({st, ed});
st = x.first, ed = x.second;
}
}
res.push_back({st, ed});
return res;
}