数学

如果 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 次
image.png
DP分析法

  • 状态表示 f[i][j]
    • 集合:所有数量为 i ,总体积不超通过 j 的物品的选法集合
    • 属性:最大价值
  • 状态计算
    • 所有不选择第 i 个物品方案中的最大值 f[i-1][j]
    • 所有选择第 i 个物品方案中的最大值 f[i-1][j-v[i]]+w[i]

朴素代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1010;
  4. int n,m;
  5. int w[N],v[N];
  6. int f[N][N];
  7. int main(){
  8. cin>>n>>m;
  9. for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
  10. for(int i=1;i<=n;i++){
  11. for(int j=0;j<=m;j++){
  12. f[i][j] = f[i-1][j];
  13. if(j>=v[i]) f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
  14. }
  15. }
  16. cout<<f[n][m];
  17. return 0;
  18. }

优化空间后代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1010;
  4. int n,m;
  5. int w[N],v[N];
  6. int f[N];
  7. int main(){
  8. cin>>n>>m;
  9. for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
  10. for(int i=1;i<=n;i++){
  11. for(int j=m;j>=v[i];j--){
  12. f[j] = max(f[j],f[j-v[i]]+w[i]);
  13. }
  14. }
  15. cout<<f[m];
  16. return 0;
  17. }

优化思路

由于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层的了。

最长上升子序列

image.png
分析:

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

image.png

枚举模拟排序

善用sscanf()函数
格式化读入数据 sscanf(a,b,c)是把a中的每个字符,按照b格式,赋值给c
例如

  1. 字符串s"03:08:20" 赋值给 int 类型的hour minue time
  2. sscanf(s.c_str(),"%d:%d:%d",&hour,&minute,&time)

相对的,sprintf(a,b,c) 把c中的字符,按照b格式,赋值给a

  1. sprintf(format_time,"%02d:%02d:%02d", day, hour, minute); //反向打印到format_time中

结构体vector

  1. // 定义结构体
  2. struct ord_struct{
  3. int ts,id;
  4. bool operator<(const ord_struct& t)const{
  5. return ts<t.ts;
  6. }
  7. };
  8. // 定义vector
  9. vector<ord_struct> ord;
  10. // 遍历
  11. for(int i=0;i<ord.size();i++){
  12. cout<<ord[i].ts<<endl;
  13. }

pair

  1. #define x first
  2. #define y second
  3. typedef pair<int,int> PII;

树状数组与线段树

树状数组

树状数组,顾名思义就是把数组用树的形式表示出来,它有如下特点

  • 在O(logn)下对根据原数组构造出的树中节点值加任何数字
  • 在O(logn)下求原数组前缀和

具体表示
image.png

  • 构造出的树也是用数组表示,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)下对根据原数组构造出的树中节点值加任何数字

  1. void add(int x, int v)
  2. {
  3. for (int i = x; i <= n; i += lowbit(i)) tr[i] += v;
  4. }

在O(logn)下求原数组前缀和

  1. int query(int x)
  2. {
  3. int res = 0;
  4. for (int i = x; i; i -= lowbit(i)) res += tr[i];
  5. return res;
  6. }

image.png

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 100010;
  4. int n,m;
  5. int tr[N],arr[N];
  6. int lowbit(int x){
  7. return x & -x;
  8. }
  9. void add(int x,int v){
  10. for(int i = x;i<=n;i+=lowbit(i)){
  11. tr[i]+=v;
  12. }
  13. }
  14. int query(int x){
  15. int res = 0;
  16. for(int i = x;i;i-=lowbit(i)){
  17. res+=tr[i];
  18. }
  19. return res;
  20. }
  21. int main(){
  22. cin>>n>>m;
  23. for(int i=1;i<=n;i++){
  24. cin>>arr[i];
  25. }
  26. for (int i = 1; i <= n; i ++ ) add(i, arr[i]);
  27. while(m--){
  28. int k,a,b;
  29. cin>>k>>a>>b;
  30. if(k==1) add(a,b);
  31. else cout<<query(b)-query(a-1)<<endl;
  32. }
  33. return 0;
  34. }

线段树

太复杂了没看。。回头补上

双指针、bfs

双指针

虽然叫双指针,但是和指针没有关系。
双指针可以想象成,数组中有两个表示位置的变量,不断更改划分的范围
常常用来优化代码,将O(n^2)简化为O(n)

  1. // 原本
  2. for(int i = 0; i < n; i ++ ){
  3. for(int j = 0; j < m; j ++ ){
  4. }
  5. }
  6. // 现在
  7. for(int i = 0, j = 0; i < n; i ++ ){
  8. if(check(j)) j++; // 如果j满足某些条件
  9. }

bfs

  • bfs 对应队列 先进先出的思想 可以找到通向某一节点的最短路径
  • dfs 对应栈 先进后出的思想 递归

image.png
bfs遍历顺序:1 2 3 4 5 6 7
我们用 队列 来进行bfs操作:
先把根节点入队
while(存在其他节点没入队 即 队列不空){
把队头关联点入队
对头出队
}

  1. 判重数组 st[] 入队时判重
  2. while(queue非空){
  3. t <- 队头
  4. for(扩展t){
  5. var = 新节点
  6. if(!st[var]){
  7. var -> 队尾
  8. }
  9. }
  10. }

image.png

  1. #include <bits/stdc++.h>
  2. #define x first
  3. #define y second
  4. using namespace std;
  5. const int N = 210;
  6. typedef pair<int, int> PII;
  7. int T;
  8. int R, C;
  9. char g[N][N];
  10. int dist[N][N];
  11. int bfs(PII start,PII end){
  12. queue<PII> que;
  13. que.push(start);
  14. memset(dist, -1, sizeof dist);
  15. dist[start.x][start.y] = 0;
  16. int dx[4] = {0, 1, 0, -1},dy[4] = {1, 0, -1 ,0};
  17. while(que.size()){
  18. auto t = que.front();
  19. que.pop();
  20. for(int i = 0; i < 4; i ++ ){
  21. int x = t.x + dx[i],y = t.y + dy[i];
  22. if(x < 0 || x >= R || y < 0 || y >= C) continue; // 出界
  23. if(g[x][y] == '#') continue; // 遇到障碍物
  24. if(dist[x][y] != -1) continue; // 走过了
  25. dist[x][y] = dist[t.x][t.y] + 1;
  26. if (end == make_pair(x, y)) return dist[x][y];
  27. que.push({x,y});
  28. }
  29. }
  30. return -1;
  31. }
  32. int main(){
  33. cin >> T;
  34. while(T -- ){
  35. cin >> R >> C;
  36. for(int i = 0; i < R; i ++ ) scanf("%s", g[i]);
  37. PII start, end;
  38. for(int i = 0; i < R; i ++ )
  39. for(int j = 0; j < C; j ++){
  40. if(g[i][j] == 'S') start = {i, j};
  41. if(g[i][j] == 'E') end = {i, j};
  42. }
  43. int distance = bfs(start, end);
  44. if(distance==-1) puts("oop!");
  45. else cout<<distance<<endl;
  46. }
  47. return 0;
  48. }

连通块问题

  • bfs 或 dfs
  • 并查集

image.png

  1. #include <bits/stdc++.h>
  2. #define x first
  3. #define y second
  4. using namespace std;
  5. typedef pair<int, int> PII;
  6. const int N = 1010;
  7. int n;
  8. int cnt;
  9. PII q[N*N];
  10. char g[N][N];
  11. bool st[N][N];
  12. int dx[4] = {-1, 0, 1, 0},dy[4] = {0, 1, 0, -1};
  13. void bfs(int i, int j, int &total, int &bound){
  14. int hh = 0, tt = 0;
  15. q[0] = {i, j};
  16. while(hh <= tt){
  17. total ++;
  18. auto t = q[hh ++];
  19. bool flag = false;
  20. for(int i = 0; i < 4; i ++ ){
  21. int x = t.x + dx[i], y = t.y + dy[i];
  22. if(x < 0 || x >= n || y < 0 || y >= n) continue;
  23. if(st[x][y]) continue;
  24. if(g[x][y] == '.'){
  25. flag = true;
  26. continue;
  27. }
  28. st[x][y] = true;
  29. q[++tt] = {x, y};
  30. }
  31. if(flag) bound ++;
  32. }
  33. }
  34. int main(){
  35. cin >> n;
  36. for(int i = 0; i < n; i ++ ) scanf("%s",g[i]);
  37. for(int i = 0; i < n; i ++ ){
  38. for(int j = 0; j < n; j ++ ){
  39. if(st[i][j] == false && g[i][j] == '#'){
  40. st[i][j] == true;
  41. int total = 0, bound = 0;
  42. bfs(i, j, total, bound);
  43. if(total == bound){
  44. cnt ++;
  45. }
  46. }
  47. }
  48. }
  49. cout << cnt;
  50. return 0;
  51. }

图论

如何存图

  • 邻接矩阵 ( 适合于稀疏图)
  • 邻接表
    • vector
    • 数组模拟单链表

求图中最长的一段距离(树的直径问题)

  1. 图中随便找一个点,找到距离这个点最远的点
  2. 对于上一步最远的点,在图中找到距离这个点最远的点,这两点之间的距离即为树的直径

    反证法证明

y 为距离 x 最远的点,u v是树的直径,假设y不是任何一条直径上的端点
image.png
有交点:因为 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必定在直径的端点上,与假设矛盾

大臣的旅费
image.png

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. const int N = 100010;
  4. int n;
  5. int dist[N];
  6. struct Edge{
  7. int id, w;
  8. };
  9. vector<Edge> h[N];
  10. void dfs(int u, int father, int distance){
  11. dist[u] = distance;
  12. for(auto node: h[u]){
  13. if(node.id != father) dfs(node.id, u, distance + node.w);
  14. }
  15. }
  16. int main(){
  17. cin >> n;
  18. int p, q, d;
  19. while(cin >> p >> q >> d){
  20. h[p].push_back({q, d});
  21. h[q].push_back({p, d});
  22. }
  23. dfs(1, -1, 0);
  24. int u = 1;
  25. for(int i = 1; i <= n; i ++ ){
  26. if(dist[i] > dist[u]){
  27. u = i;
  28. }
  29. }
  30. dfs(u, -1, 0);
  31. for(int i = 1; i <= n; i ++ ){
  32. if(dist[i] > dist[u]){
  33. u = i;
  34. }
  35. }
  36. int s = dist[u];
  37. printf("%lld",s*10 + s+s*(s-1ll)/2);
  38. return 0;
  39. }