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算法:
思路:边的权值排序,因为要优先选最小的边加入到生成树里。
遍历排序后的边,如果边收尾的两个节点在同一个集合中,说明如果连上这条边图中会出现环;如果边首尾的两个节点不在同一个集合,加入到最小生成树,并把两个节点加入同一个集合