算法 模版
cin cout加快读取速度:
ios::sync_with_stdio(false);
高精度*高精度
vector<int> mul(vector<int>& a, vector<int>& b) {
vector<int>c(b.size()+a.size()+5,0);
for (int i = 0; i < a.size(); i++) {
for (int j = 0; j < b.size(); j++) {
c[i + j] += a[i] * b[j];
}
}
for (int i = 0; i < c.size(); i++) {
if (c[i] > 9) {
c[i + 1] += c[i] / 10;
c[i] = c[i] % 10;
}
}
while (c.size() > 1 && c.back() == 0)c.pop_back();
return c;
}
高精度+高精度
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;
}
二分:
bool check(int x) {/* ... */} // 检查x是否满足某种性质
// 求右边界时用,满足右区间性质为true
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;
}
// 求左边界时用,满足左区间性质为true
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;
}
int res=upper_bound(num,num+n,q)-num;
int res=lower_bound(num,num+n,q)-num;
upper寻找第一个大于q的数的地址,lower寻找第一个大于等于q的数的地址。在"algorithm"库中。
滑动窗口:
//进窗口
......
//处理结果
......
//出窗口
......
动态规划:
- 确定dp数组(dp table)以及下标的含义
- 确定递推公式
- dp数组如何初始化
- 确定遍历顺序
- 举例推导dp数组
- 01背包:物品只有一个。dp[i][j]从前i个物品中选,容量为j时选择的最大价值是dp[i][j]。
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);区别就是对i物品选与不选。
- 完全背包:每个物品无数个。dp[i][j]定义一样。
dp[i][j]=max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);
两者在选择i物品上有区别,
dfs:
深搜三步曲:
1.确定递归函数,参数
2。确认终止条件
3.处理目前搜索节点出发的路径
void dfs(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本节点所连接的其他节点) {
处理节点;
dfs(图,选择的节点); // 递归
回溯,撤销处理结果
}
}
bfs:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 表示四个方向
// grid 是地图,也就是一个二维数组
// visited标记访问过的节点,不要重复访问
// x,y 表示开始搜索节点的下标
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y) {
queue<pair<int, int>> que; // 定义队列
que.push({x, y}); // 起始节点加入队列
visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
while(!que.empty()) { // 开始遍历队列里的元素
pair<int ,int> cur = que.front(); que.pop(); // 从队列取元素
int curx = cur.first;
int cury = cur.second; // 当前节点坐标
for (int i = 0; i < 4; i++) { // 开始想当前节点的四个方向左右上下去遍历
int nextx = curx + dir[i][0];
int nexty = cury + dir[i][1]; // 获取周边四个方向的坐标
if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue; // 坐标越界了,直接跳过
if (!visited[nextx][nexty]) { // 如果节点没被访问过
que.push({nextx, nexty}); // 队列添加该节点为下一轮要遍历的节点
visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
}
}
}
}
快排:
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);
}
归并排序:
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 (int i = l,j=0;i<=r;i++,j++) q[i] = tmp[j];
}
前缀与差分:
a[n]是b[n]的前缀和数组,b[n]是a[n]的差分数组。
a[n]=b[1]+b[2]+...+b[n]。b[n]=a[n]-a[n-1]。
如果对a数组l到r均+c,那么只需对b数组b[l]+c,b[r+1]-c。
如果求b数组从l到r的和,那么只需查询a[r]-a[l]。
并查集:
1.将两个元素添加到同一个集合中。
2.判断两个元素在不在同一个集合中。
int n = 1005; // n根据题目中节点数量而定,一般比节点数量大一点就好
vector<int> father = vector<int> (n, 0); // C++里的一种数组结构
// 并查集初始化
void init() {
for (int i = 0; i < n; ++i) {
father[i] = i;
}
}
// 并查集里寻根的过程
int find(int u) {
if (u == father[u]) return u;
else return father[u] = find(father[u]); // 路径压缩
}
// 判断 u 和 v是否找到同一个根
bool isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 将v->u 这条边加入并查集
void join(int u, int v) {
u = find(u); // 寻找u的根
v = find(v); // 寻找v的根
if (u == v) return ; // 如果发现根相同,则说明在一个集合,不用两个节点相连直接返回
father[v] = u;
}
最小生成树:
prim三部曲:
1.选距离生成树最近节点
2.最近节点加入生成树
3.更新非生成树节点到生成树的距离(即更新minDist数组)
Kruskal算法:
思路:边的权值排序,因为要优先选最小的边加入到生成树里。
遍历排序后的边,如果边收尾的两个节点在同一个集合中,说明如果连上这条边图中会出现环;如果边首尾的两个节点不在同一个集合,加入到最小生成树,并把两个节点加入同一个集合