数学
如果 a,b 均是正整数且互质,那么由 ax+by,x≥0,y≥0 不能凑出的最大数是 (a−1)(b−1)−1。
c++中,a/b 的结果默认下取整,使用 ceil() 可以获得上取整,但是要记得类型转换将 double 转为 int 或者,用(a+b-1)/b 下取整的结果表示 a/b 的上取整
一维数组转二维数组 x/3 和 x%3
动态规划
01 背包问题
01 表示使用 1 次或者使用 0 次
DP分析法
- 状态表示 f[i][j]
- 集合:所有数量为 i ,总体积不超通过 j 的物品的选法集合
- 属性:最大价值
- 状态计算
- 所有不选择第 i 个物品方案中的最大值 f[i-1][j]
- 所有选择第 i 个物品方案中的最大值 f[i-1][j-v[i]]+w[i]
朴素代码
#include<bits/stdc++.h>using namespace std;const int N = 1010;int n,m;int w[N],v[N];int f[N][N];int main(){cin>>n>>m;for(int i=1;i<=n;i++) cin>>v[i]>>w[i];for(int i=1;i<=n;i++){for(int j=0;j<=m;j++){f[i][j] = f[i-1][j];if(j>=v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);}}cout<<f[n][m];return 0;}
优化空间后代码
#include<bits/stdc++.h>using namespace std;const int N = 1010;int n,m;int w[N],v[N];int f[N];int main(){cin>>n>>m;for(int i=1;i<=n;i++) cin>>v[i]>>w[i];for(int i=1;i<=n;i++){for(int j=m;j>=v[i];j--){f[j] = max(f[j],f[j-v[i]]+w[i]);}}cout<<f[m];return 0;}
优化思路
由于j是从大到小循环的,假设j = 5; j >= 2; j –; 也就是这一层(第i层)会先算出来f[5],(注意这个时候,第i层正在算f[5], 也就是说第i层没有更新f[4] f[3],也就是此时的f[4] f[3] 是i - 1层的)那么算f[5]要用到比他更小的f[4]或者f[3]也就是i - 1层的了。
最长上升子序列

分析:
- 状态表示 f[i]
- 集合:所有以a[i] 结尾的严格单调上升子序列
- 属性:Max/Min/数量
- 状态计算

枚举模拟排序
善用sscanf()函数
格式化读入数据 sscanf(a,b,c)是把a中的每个字符,按照b格式,赋值给c
例如
将 字符串s"03:08:20" 赋值给 int 类型的hour minue timesscanf(s.c_str(),"%d:%d:%d",&hour,&minute,&time)
相对的,sprintf(a,b,c) 把c中的字符,按照b格式,赋值给a
sprintf(format_time,"%02d:%02d:%02d", day, hour, minute); //反向打印到format_time中
结构体vector
// 定义结构体struct ord_struct{int ts,id;bool operator<(const ord_struct& t)const{return ts<t.ts;}};// 定义vectorvector<ord_struct> ord;// 遍历for(int i=0;i<ord.size();i++){cout<<ord[i].ts<<endl;}
pair
#define x first#define y secondtypedef pair<int,int> PII;
树状数组与线段树
树状数组
树状数组,顾名思义就是把数组用树的形式表示出来,它有如下特点
- 在O(logn)下对根据原数组构造出的树中节点值加任何数字
- 在O(logn)下求原数组前缀和
具体表示
- 构造出的树也是用数组表示,tr[]
- tr[x]的层数k = x的二进制表示最后有几个零就在第几层
- tr[x]的值表示从 (x-2^k, x] 累加,2^k 为 lowbit(x) 即x&-x,lowbit(x) 为 x 的父节点
- 所以,tr[x]的值表示(x-lowbit(x),x] 累加
在O(logn)下对根据原数组构造出的树中节点值加任何数字
void add(int x, int v){for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;}
在O(logn)下求原数组前缀和
int query(int x){int res = 0;for (int i = x; i; i -= lowbit(i)) res += tr[i];return res;}

#include<bits/stdc++.h>using namespace std;const int N = 100010;int n,m;int tr[N],arr[N];int lowbit(int x){return x & -x;}void add(int x,int v){for(int i = x;i<=n;i+=lowbit(i)){tr[i]+=v;}}int query(int x){int res = 0;for(int i = x;i;i-=lowbit(i)){res+=tr[i];}return res;}int main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>arr[i];}for (int i = 1; i <= n; i ++ ) add(i, arr[i]);while(m--){int k,a,b;cin>>k>>a>>b;if(k==1) add(a,b);else cout<<query(b)-query(a-1)<<endl;}return 0;}
线段树
太复杂了没看。。回头补上
双指针、bfs
双指针
虽然叫双指针,但是和指针没有关系。
双指针可以想象成,数组中有两个表示位置的变量,不断更改划分的范围
常常用来优化代码,将O(n^2)简化为O(n)
// 原本for(int i = 0; i < n; i ++ ){for(int j = 0; j < m; j ++ ){}}// 现在for(int i = 0, j = 0; i < n; i ++ ){if(check(j)) j++; // 如果j满足某些条件}
bfs
- bfs 对应队列 先进先出的思想 可以找到通向某一节点的最短路径
- dfs 对应栈 先进后出的思想 递归

bfs遍历顺序:1 2 3 4 5 6 7
我们用 队列 来进行bfs操作:
先把根节点入队
while(存在其他节点没入队 即 队列不空){
把队头关联点入队
对头出队
}
判重数组 st[] 入队时判重while(queue非空){t <- 队头for(扩展t){var = 新节点if(!st[var]){var -> 队尾}}}

#include <bits/stdc++.h>#define x first#define y secondusing namespace std;const int N = 210;typedef pair<int, int> PII;int T;int R, C;char g[N][N];int dist[N][N];int bfs(PII start,PII end){queue<PII> que;que.push(start);memset(dist, -1, sizeof dist);dist[start.x][start.y] = 0;int dx[4] = {0, 1, 0, -1},dy[4] = {1, 0, -1 ,0};while(que.size()){auto t = que.front();que.pop();for(int i = 0; i < 4; i ++ ){int x = t.x + dx[i],y = t.y + dy[i];if(x < 0 || x >= R || y < 0 || y >= C) continue; // 出界if(g[x][y] == '#') continue; // 遇到障碍物if(dist[x][y] != -1) continue; // 走过了dist[x][y] = dist[t.x][t.y] + 1;if (end == make_pair(x, y)) return dist[x][y];que.push({x,y});}}return -1;}int main(){cin >> T;while(T -- ){cin >> R >> C;for(int i = 0; i < R; i ++ ) scanf("%s", g[i]);PII start, end;for(int i = 0; i < R; i ++ )for(int j = 0; j < C; j ++){if(g[i][j] == 'S') start = {i, j};if(g[i][j] == 'E') end = {i, j};}int distance = bfs(start, end);if(distance==-1) puts("oop!");else cout<<distance<<endl;}return 0;}
连通块问题
- bfs 或 dfs
- 并查集

#include <bits/stdc++.h>#define x first#define y secondusing namespace std;typedef pair<int, int> PII;const int N = 1010;int n;int cnt;PII q[N*N];char g[N][N];bool st[N][N];int dx[4] = {-1, 0, 1, 0},dy[4] = {0, 1, 0, -1};void bfs(int i, int j, int &total, int &bound){int hh = 0, tt = 0;q[0] = {i, j};while(hh <= tt){total ++;auto t = q[hh ++];bool flag = false;for(int i = 0; i < 4; i ++ ){int x = t.x + dx[i], y = t.y + dy[i];if(x < 0 || x >= n || y < 0 || y >= n) continue;if(st[x][y]) continue;if(g[x][y] == '.'){flag = true;continue;}st[x][y] = true;q[++tt] = {x, y};}if(flag) bound ++;}}int main(){cin >> n;for(int i = 0; i < n; i ++ ) scanf("%s",g[i]);for(int i = 0; i < n; i ++ ){for(int j = 0; j < n; j ++ ){if(st[i][j] == false && g[i][j] == '#'){st[i][j] == true;int total = 0, bound = 0;bfs(i, j, total, bound);if(total == bound){cnt ++;}}}}cout << cnt;return 0;}
图论
如何存图
- 邻接矩阵 ( 适合于稀疏图)
- 邻接表
- vector
- 数组模拟单链表
求图中最长的一段距离(树的直径问题)
- 图中随便找一个点,找到距离这个点最远的点
- 对于上一步最远的点,在图中找到距离这个点最远的点,这两点之间的距离即为树的直径
反证法证明
y 为距离 x 最远的点,u v是树的直径,假设y不是任何一条直径上的端点
有交点:因为 y 是距离x最远的点,所以xy>=xa+av 即 ay >= av,两边同时加au,得到ay+au >= uv,所以y必定在直径的端点上,与假设矛盾
无交点:因为 y 是距离x最远的点,所以xy>=ya+ab+bu => ax>=ub,xy>=xa+ab+bv => ay>= bv,所以y必定在直径的端点上,与假设矛盾
大臣的旅费
#include <bits/stdc++.h>using namespace std;const int N = 100010;int n;int dist[N];struct Edge{int id, w;};vector<Edge> h[N];void dfs(int u, int father, int distance){dist[u] = distance;for(auto node: h[u]){if(node.id != father) dfs(node.id, u, distance + node.w);}}int main(){cin >> n;int p, q, d;while(cin >> p >> q >> d){h[p].push_back({q, d});h[q].push_back({p, d});}dfs(1, -1, 0);int u = 1;for(int i = 1; i <= n; i ++ ){if(dist[i] > dist[u]){u = i;}}dfs(u, -1, 0);for(int i = 1; i <= n; i ++ ){if(dist[i] > dist[u]){u = i;}}int s = dist[u];printf("%lld",s*10 + s+s*(s-1ll)/2);return 0;}
