1. 二分查找
int check(int m);
int l = 1, r = n;
while (l <= r)
{
int mid = (r + l)/2;
if (check(mid) > k)
{
r=mid-1;
}
else
{
l=mid+1;
}
}
// 在一维区间上搜索时可以采用, 如 i in (1, n), i in 整数
2. DFS
int a[N], b[M], K, ans = 0;
bool found, bad, good, vis[N][M];
const int mov[2][4] = {1, 0, -1, 0, 0, 1, 0, -1};
void dfs(int x, int y, int k)
{
if (x < 1 || y < 1 || x > n || y > m}
return;
if (bad || found)
return;
// 该点有效, 此处填写代码计算当前点
{ }
// 接下来决定停止搜索树还是继续搜索
if (x == n && y == m && good)
{
found = true;
ans = k;
return;
}
for (int i = 0; i < 4; i++)
{
int xx = x + mv[0][i], y=y+mv[1][i];
if (vis[xx][yy])
continue;
// 保存现场
a[xx]--, b[yy]--, K--, vis[xx][yy]=true;
dfs(xx, yy, k+1);
a[xx]++, b[yy]++, K++, vis[xx][yy]=false;
// 假装无事发生
}
}
int main()
{
found = false, vis[1][1]=true;
dfs(1,1,1) // 从(1,1)开始搜索, 路径长1
}
3. BFS
# include <queue>
typedef pair<int, int> PII;
queue<PII> q;
const int mov[2][4] = {1,0,-1,0,0,1,0,-1};
int n, m, ans;
bool found, good, bad, vis[N][M];
void bfs(int x, int y)
{
while(!q.empty())
{
auto t = q.front();
q.pop();
for (int i = 0; i < 4; i++)
{
int xx = x + mov[0][i], yy=y+mov[1][i];
if (xx < 1 || yy < 1 || x > n || y > m)
continue;
if (bad || vis[xx][yy])
continue;
// 当前点有效, 对当前点进行计算
{ }
vis[xx][yy] = true;
q.push({xx,yy});
// 接着走
}
}
}
4. 动态规划
#include <algorithm>
int dp[N][M], ans, dx;
// 初始化, 如
dp[1][1] = 1;
for (int i = 1; i <= len1; i++)
{
for (int j = 1; j <= len2; j++)
{
// 转移方程, 如
dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
// 自顶向下的写法, 也可以写成递推:
// dp[i+dx][j] = dp[i][j+dx] = dp[i][j];
}
}
// 处理可以利用先前状态的问题, 减少重复搜索, 多项式复杂度
5. 简单数据结构
#include <cmath>
// 数组存储完全二叉树
int tree[N];
for (int i = 1; i <= n; i++)
{
cin >> tree[i];
int depth = ceil(log(i+1)/log(2));
}
void postOrder(int root) // root表示序号, 简单的树其实就是给数组加上了父子信息
{
if (tree[2*p])
postOrder(2*p) // 左
if (tree[2*p+1])
postOrder(2*p+1) // 右
cout << tree[p];
}
# include <vector>
// 图的表示(邻接矩阵)
int n, m;
const int maxM;
vector <int> g[maxM];
for (int i = 1; i <= m; i++) // m条边
{
int x, y;
cin >> x >> y;
g[x].push_back(y);
g[y].push_back(x);
}
6. 优化方法
6.1 前缀和
int s[N] = {0};
for (int i = 1; i <= n; i++)
{
cin >> s[i];
s[i] = s[i-1];
}
// s(i, j) = s[j] - s[i], 避免O(n2)双循环
6.2 并查集(dsu on tree)
int parent[N], items[N];
for (int i = 0; i < N; i++)
{
parent[i] = i; // 初始化, 祖宗 (目标)是自己, 自己可用
}
int find(int x)
{
if (parent[x] != x) // 祖宗不是自身, 该点不可用, 向上寻找
parent[x] = find(parent[x]); // 每次查找进行路径压缩, 减少树的高度
return parent[x];
}
int main()
{
//
for (int it : items)
{
it = find(it);
// 更新祖宗(目标)节点, 如
parent[it] = it + 1;
}
}
// 复杂度近乎O(1), 因为大部分时候树的高度差是1
6.3 滑动窗口(同向双指针)
#include <algorithm>
int arr[N];
long s = 0, target, ans = N;
for (int l = 1, r = 1; r <= n; r++)
{
// 对子序列[l,r]操作, 以求和为例
s += arr[r];
while (s >= target) // 收缩左边界, 而后延长右边界
{
ans = min(ans, r - l + 1);
// 收缩左边界
s -= arr[l++];
}
// 右动, 然后左动, 左边死了再动右边
}
// 另一种写法, 两个指针先都找好位置了再统计, 有点像前缀和
// 左右一起准备好, 然后找下一段
// 这种写法关注元素, 上一种更关注子序列整体
int t[M];
for (int i = 1, l = 1, r = 1; i <= m; i++)
{
while (l <= n && arr[l] < t[i])
l++;
while (r <= n && arr[r] <= t[i] + target)
r++;
ans += r-l;
}
// r走一遍, l走一遍, 复杂度O(n2)变成O(2n)
6.4 对向双指针
#include <vector>
vector<pair<int, int>> ans;
int arr[N], Found = 0, target;
// 可能需要有序数组
// sort(arr, arr+n);
int l = 1, r = n;
while(!Found)
{
// 计算左右两点, 如
int s = arr[l] + arr[r];
if (target)
ans.push_back({l,r});
else
s > target ? r-- : l++; // 根据条件移动右指针或左指针
}
// 双指针利用有序性, O(n2)变 O(n)
7. 位运算
// 按位 &与, |或, ^亦或, ~取反
int n, x, k;
x << n; // 左移n位
x >> n; // 右移n位
// 逻辑右移
x|1; // 最后一位变为1
(x|1)-1; // 最后一位变为0
x^1; // 最后一位取反
x|(1<<(k-1)) // 右数第k位变1
x& ~(1<<(k-1)) // 右数第k位变0
x^(1<<(k-1)) // 右数第k位取反
x&(1<<k -1) // 取末k位数字
x>>(k-1) &1 // 取右数第k位数字
x&(x+1) // 把右起连续的1变0, 前面的不变, 如010111->010000
x|(x+1) // 把右起第一个0变1
void swap(int &a, int &b)
{
a ^= b;
b ^= a;
a ^= b;
}
int change(int a)
{
return ~a +1;
}
// 统计二进制1个数
int count(int x){
int cnt = 0;
while(x){
cnt += x & 1;
x = x >> 1;
}
return cnt;
}
8. 数论
8.1 gcd&lcm
int gcd(int a, int b)
{
while (b != 0)
{
int t = b;
b = a % b;
a = temp;
}
return a;
}
int lcm (int a, int b)
{
return (a*b) / gcd(a, b);
}
8.2 筛法判断素数
#include <vector>
vector <int> pms;
bool isPrime[N];
fill(isPrime + 1, isPrime + N + 1, true);
void isPm()
{
for (int i = 2; i*i <=n; i++)
{
if (isPrime[i])
{
pms.push_back(i);
for (int j = i*i; j <=n; j+=i)
isPrime[j] = false;
}
}
}
8.3 求质因子
typedef vector<pair<int, int>> vpii;
vpii divide(int n)
{
vpii res;
for (int i= 2; i*i <= n; i++)
{
if (n%i==0)
{
int j = 0;
while(n%i==0)
{
n/=i;
j++;
}
res.push_back({i,j});
}
}
}