基本算法的一些模板
基本算法
导
数据范围反推算法内容(c++)
位
# << 左移
二进制左移,填充0
3 << 2 === 3 * 2 * 2
# >> 右移
正数填充0,负数填充1,因此,右移负数始终为负数,正数始终为正数
27 >> 3 === 24 / 3 / 3
# >>> 无符号右移
正数右移补0, 负数也补0,因此负数会转为正数,负数会达到int最大值
-1 >>> 1 === 2147483647
16 >>> 2 === 16/2/2
# & 与
都为1时为1,其余情况为0
# | 或
有一个1则值为1,全为0值为0
# ^ 异或
相同为0, 不同为1
# ~
0变1,1变0
# 原码,反码,补码
• 第一位0,1决定正负,其余位是数字的大小,绝对值相同的数字除第一位外的其余位是相同
• 正数:原码、反码、补码全部相同
• 负数:
○ 反码:除第一位外,其余位取反
补码:反码+1
二分
int bsearch_1(int l, int r)
{
while (l < r)
{
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
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;
}
double bsearch_3(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;
}
高精
高精度加法
#include <iostream>
#include <vector>
using namespace std;
vector<int> add(vector<int> a, vector<int> b) {
vector<int> c;
int la = a.size(), lb = b.size();
if (la < lb) return add(b, a);
int t = 0;
for (int i = 0; i < la; ++i) {
t += a[i];
if (i < lb) t += b[i];
c.push_back(t % 10);
t /= 10;
}
if (t) c.push_back(t);
return c;
}
int main(void) {
vector<int> a, b;
string A, B;
cin >> A >> B;
for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');
for (int i = B.size() - 1; i >= 0; --i) b.push_back(B[i] - '0');
vector<int> c = add(a, b);
for (int i = c.size() - 1; i >= 0; --i) cout << c[i];
return 0;
}
高精度减法
#include <iostream>
#include <vector>
using namespace std;
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;
}
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;
}
int main(void) {
string A, B;
cin >> A >> B;
vector<int> a, b, c;
for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');
for (int i = B.size() - 1; i >= 0; --i) b.push_back(B[i] - '0');
if (cmp(a, b)) c = sub(a, b);
else c = sub(b, a), cout << '-';
for (int i = c.size() - 1; i >= 0; --i) cout << c[i];
return 0;
}
高精度乘法
#include <iostream>
#include <vector>
using namespace std;
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;
}
int main(void) {
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');
vector<int> c = mul(a, b);
for (int i = c.size() - 1; i >= 0; --i) cout << c[i];
return 0;
}
高精度除法和取余
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
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;
}
int main(void) {
string A;
int b;
vector<int> a, c;
cin >> A >> b;
for (int i = A.size() - 1; i >= 0; --i) a.push_back(A[i] - '0');
int r;
c = div(a, b, r);
for (int i = c.size() - 1; i >= 0; --i) cout << c[i];
cout << endl << r;
return 0;
}
双指针
最长连续不重复子序列
/*给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。*/
#include <iostream>
using namespace std;
const int N = 100010;
int a[N], st[N], n;
int main(void) {
cin >> n;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
int res = 0;
for (int i = 1, j = 1; i <= n; ++i) {
st[a[i]]++;
while (j < i && st[a[i]] > 1) st[a[j++]]--;
res = max(res, i - j + 1);
}
cout << res;
return 0;
}
数组元素的目标和
/* 数组a,b递增,求ai+bj==x时i,j下标,注意:模板规定下标从0开始 */
#include <iostream>
using namespace std;
const int N = 100010;
int a[N], b[N];
int n, m, x;
int main(void) {
scanf("%d%d%d", &n, &m, &x);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
for (int i = 1; i <= m; ++i) scanf("%d", b + i);
for (int i = 1, j = m; i <= n; ++i) {
while (j >= 1 && a[i] + b[j] > x) j--;
if (a[i] + b[j] == x) {
printf("%d %d", i - 1, j - 1);
break;
}
}
return 0;
}
判断子序列
数组a是否为数组b的子序列(指按原有次序排列)
#include <iostream>
using namespace std;
const int N = 100010;
int a[N], b[N], n, m;
int main(void) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i) scanf("%d", a + i);
for (int i = 1; i <= m; ++i) scanf("%d", b + i);
int i = 1;
for (int j = 1; j <= m; ++j) {
if (a[i] == b[j]) i++;
}
if (i == n + 1) puts("Yes");
else puts("No");
return 0;
}
离散化
求区间和(保序)
记得开多倍数组
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 100010;
vector<PII> add, query;
vector<int> all;
int a[N * 3], s[N * 3];
int n, q;
int find(int x) {
int l = 0, r = all.size() - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (all[mid] < x) l = mid;
else r = mid - 1;
}
return r + 1;
}
int main(void) {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; ++i) {
int x, v;
scanf("%d%d", &x, &v);
add.push_back({x, v});
all.push_back(x);
}
for (int i = 1; i <= q; ++i) {
int l, r;
scanf("%d%d", &l, &r);
query.push_back({l, r});
all.push_back(l);
all.push_back(r);
}
sort(all.begin(), all.end());
all.erase(unique(all.begin(), all.end()), all.end());
for (int i = 0; i < add.size(); ++i) {
int x = add[i].first, v = add[i].second;
x = find(x);
a[x] += v;
}
for (int i = 1; i <= all.size(); ++i) s[i] = s[i - 1] + a[i];
for (int i = 0; i < query.size(); ++i) {
int l = query[i].first, r = query[i].second;
l = find(l), r = find(r);
printf("%d\n", s[r] - s[l - 1]);
}
return 0;
}
其他
区间合并
给定 n个区间[l, r],要求合并所有有交集的区间。
#include <iostream>
#include <vector>
#include <algorithm>
#define l first
#define r second
using namespace std;
typedef pair<int, int> PII;
vector<PII> segs;
int n;
int merge() {
vector<PII> res;
int l = -2e9, r = -2e9;
for (int i = 0; i < segs.size(); ++i) {
PII t = segs[i];
if (r < t.l) {
if (r != -2e9) res.push_back({l, r});
l = t.l, r = t.r;
}
else
r = max(r, t.r);
}
if (l != -2e9) res.push_back({l, r});
return res.size();
}
int main(void) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
int l, r;
scanf("%d%d", &l, &r);
segs.push_back({l, r});
}
sort(segs.begin(), segs.end());
cout << merge();
return 0;
}
动态中位数
依次读入一个整数序列,每当已经读入的整数个数为奇数时,输出已读入的整数构成的序列的中位数。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
int main()
{
int T;
scanf("%d", &T);
while (T -- )
{
int n, m;
scanf("%d%d", &m, &n);
printf("%d %d\n", m, (n + 1) / 2);
priority_queue<int> down;
priority_queue<int, vector<int>, greater<int>> up;
int cnt = 0;
for (int i = 1; i <= n; i ++ )
{
int x;
scanf("%d", &x);
if (down.size() == 0 || x <= down.top()) down.push(x);
else up.push(x);
if (down.size() > up.size() + 1) up.push(down.top()), down.pop();
if (up.size() > down.size()) down.push(up.top()), up.pop();
if (i % 2)
{
printf("%d ", down.top());
if ( ++ cnt % 10 == 0) puts("");
}
}
if (cnt % 10) puts("");
}
return 0;
}
增减序列
给定一个长度为 n 的数列 a1,a2,…,an,每次可以选择一个区间 [l,r],使下标在这个区间内的数都加一或者都减一。
求至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列可能有多少种。
第一行输出最少操作次数。
第二行输出最终能得到多少种结果。
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 100010;
int a[N], d[N];
int n;
int main(void) {
cin >> n;
for (int i = 1; i <= n; ++i) cin >> a[i];
for (int i = 1; i <= n; ++i) d[i] = a[i] - a[i - 1];
LL p = 0, q = 0;
for (int i = 2; i <= n; ++i)
if (d[i] > 0) p += d[i];
else q -= d[i];
cout << max(p, q) << endl;
cout << (abs(p - q) + 1) ;
return 0;
}