前言:
本系列是学习了董晓老师所讲的知识点做的笔记
董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (bilibili.com)
动态规划系列(还没学完)
【董晓算法】动态规划之线性DP问题-CSDN博客
【董晓算法】动态规划之背包DP问题(2024.5.11)-CSDN博客
【董晓算法】动态规划之背包DP与树形DP-CSDN博客
字符串系列()
【董晓算法】竞赛常用知识之字符串1-CSDN博客
【董晓算法】竞赛常用知识之字符串2-CSDN博客
数据结构系列(未学完)
【董晓算法】竞赛常用知识点之数据结构1-CSDN博客
STL容器
图的存储
领接矩阵
应用:适用于点数不多的稠密图
int w[N][N];//边权
int vis[N];
void dfs(int u){vis[u]=true;for(int v=1;v<=n;v++)if(w[u][v]){printf("%d,%d,%d\n",u,v,w[u][v]);if(vis[v]) continue;dfs(v);}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){cin>>a>>b>>c;w[a][b]=c; }dfs(1);return 0;
}
边集数组
应用:在 Kruskal算法中,需要将边按边权排序,直接存边
struct edge{int u,v,w;
}e[M];//边集
int vis[N];
void dfs(int u){vis[u]=true;//标记为使用过for(int i=1;i<=m;i++)if(e[i].u==u){int v=e[i].v,w=e[i].w;printf("%d,%d,%d\n",u,v,w);if(vis[v]) continue;dfs(e[i].v);}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){cin>>a>>b>>c;e[i]={a,b,c};//边的存储方式// e[i]={b,a,c};}dfs(1);return 0;
}
if(e[i].u==u)
作用:检查这条边的起始顶点 e[i].u 是否与当前正在考虑的顶点 u 相同
邻接表
出边数组e[u][i]存储u点的所有出边的{终点v,边权w}
struct edge{int v,w;};
vector<edge> e[N];//边集
void dfs(int u,int fa){for(auto ed : e[u]){int v=ed.v, w=ed.w;if(v==fa) continue;printf("%d,%d,%d\n",u,v,w);dfs(v, u);}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){ cin>>a>>b>>c,e[a].push_back({b,c});e[b].push_back({a,c});}dfs(1, 0);return 0;
}
链式邻接表
边集数组 e[j] 存储第j条边的 {起点u,终点v,边权w]
表头数组 h[u][i] 存储u点的所有出边的编号
应用:各种图,能处理反向边
struct edge{int u,v,w;};
vector<edge> e;//边集
vector<int> h[N];//点的所有出边
void add(int a,int b,int c){e.push_back({a,b,c});h[a].push_back(e.size()-1);
}
void dfs(int u,int fa){for(int i=0;i<h[u].size();i++){int j=h[u][i];int v=e[j].v,w=e[j].w;if(v==fa) continue;printf("%d,%d,%d\n",u,v,w);dfs(v,u);}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++){cin>>a>>b>>c,add(a,b,c);add(b,a,c);} dfs(1, 0);return 0;
}
链式前向星
一个表头数组悬挂多个链表
边集数组 e[i]存储第i条出边的{终点v,边权w,下一条诀ne}
表头数组加存储u点的第一条出边的编号
边的编号 idx 可取 0,1,2,3 ..
struct edge{int v,w,ne;};
edge e[M];//边集
int idx,h[N];//点的第一条出边
void add(int a,int b,int c){e[idx]={b,c,h[a]};h[a]=idx++;
}
void dfs(int u,int fa){for(int i=h[u];~i;i=e[i].ne){int v=e[i].v, w=e[i].w;if(v==fa) continue;printf("%d,%d,%d\n",u,v,w);dfs(v,u);}
}
int main(){cin>>n>>m;memset(h,-1,sizeof h);for(int i=1;i<=m;i++){cin>>a>>b>>c,add(a,b,c);add(b,a,c);} dfs(1, 0);return 0;
}
DFS
int n, m, a, b, c;
vector<int> e[N];
void dfs(int u, int fa){for(auto v : e[u]){if(v==fa) continue;dfs(v, u);}
}
int main(){cin>>n>>m;for(int i=1;i<=m;i++)cin>>a>>b, e[a].push_back(b),e[b].push_back(a);dfs(1, 0);return 0;
}
迷宫方案数
P1605 迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;const int N = 10;
int m,n,t,sx,sy,fx,fy,a,b,ans;
int g[N][N];
int dx[4]={-1,0,1, 0},dy[4]={ 0,1,0,-1};void dfs(int x, int y){if(x==fx&&y==fy){ans++;return;}for(int i=0; i<4; i++){int a=x+dx[i], b=y+dy[i];//四个方位寻找if(a<1||a>n||b<1||b>m||g[a][b])continue;//退出条件g[a][b]=1; //锁定现场dfs(a, b);//递归g[a][b]=0; //恢复现场}
}
int main(){cin>>n>>m>>t>>sx>>sy>>fx>>fy;for(int i=0;i<t;i++)cin>>a>>b, g[a][b]=1;g[sx][sy]=1;dfs(sx,sy);cout<<ans;return 0;
}
跳马方案数
P1644 跳马问题 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
学会设置偏移量(探照灯)
学会判越界,判重,回溯
int dx[4]={2,1,-1,-2};
int dy[4]={1,2, 2, 1};
void dfs(int x, int y){if(x==n&&y==m){ans++;return;}for(int i=0; i<4; i++){int a=x+dx[i], b=y+dy[i];if(a<0||a>n||b>m) continue;// printf("(%d,%d)\n",a,b);dfs(a,b);}
}
void dfs(int x, int y){if(x==n&&y==m){ans++;return;}for(int i=0; i<4; i++){int a=x+dx[i], b=y+dy[i];//四个方位寻找if(a<0||a>n||b>m)continue;//退出条件dfs(a, b);//递归}
}
八皇后
P1219 [USACO1.5] 八皇后 Checker Challenge - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
学会按行搜索状态空间
学会标记对角线的技巧:p[i+j],q[i-j+n]对角线与行列的映射关系对角线技巧也可以通过列方程获得
int n, ans;
int pos[N],c[N],p[N],q[N];
void print(){if(ans<=3){//题目中只需要三个for(int i=1;i<=n;i++)printf("%d ",pos[i]);puts("");}
}
void dfs(int i){if(i>n){//终止条件ans++; print(); return;}for(int j=1; j<=n; j++){if(c[j]||p[i+j]||q[i-j+n])continue;//退出条件pos[i]=j; //记录第i行放在了第j列c[j]=p[i+j]=q[i-j+n]=1; //宣布占领dfs(i+1);c[j]=p[i+j]=q[i-j+n]=0; //恢复现场}
}
int main(){cin >> n;dfs(1);cout << ans;return 0;
}
水坑计数
判重技巧:节点变性
P1596 [USACO10OCT] Lake Counting S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
char g[N][N];
int dx[8]={-1,-1,-1,0,1,1,1,0};
int dy[8]={-1,0,1,1,1,0,-1,-1};void dfs(int x,int y){g[x][y]='.';for(int i=0;i<8;i++){int a=x+dx[i],b=y+dy[i];if(a<0||a>=n||b<0||b>=m)continue;if(g[a][b]=='.')continue;dfs(a,b);}
}
BFS
vector<int> e[N];
int vis[N];
queue<int> q;void bfs( ){vis[1]=1; q.push(1); //访问该点while(q.size()){//循环条件int x=q.front(); q.pop();//取对头元素for(auto y : e[x]){//遍历元素if(vis[y]) continue;//如果遍历过 就continuevis[y]=1; q.push(y);}}
}
迷宫最短路
int n,g[N][N];
struct Node{int x,y;};
npde pre[N][N];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};void bfs (int x,int y){queue<Node> q;q.push({x,y});g[x][y] = 1;while(q.size()){auto u=q.front(); q.pop();for(int i = 0; i < 4; i ++){int a = u.x+dx[i],b = u.y+dy[i];if(a<0||a>=n||b<0||b>=n)continue;if(g[a][b])continue;g[a][b] = 1;//打标记pre[a][b] = u;//记录路径q.push({a,b});} }
}
void print(int x,int y){if(x==0%%y==0) return;node p=pre[x][y];print(p.x,p.y);//递归printf("%d %d\n",x,y);
}
int main(){cin >> n;for(int i = 0; i < n; i ++)for(int j = 0; j < n; j ++)scanf("%d",&g[i][j]);bfs(0,0);puts("0 0");print(n-1,n-1);return 0;
}
矩阵距离-多源BFS问题
开始就把所有源头压入队列,其余同单源BFS问题一样。
char g[N][N];
struct Node{int x,y;};
int dis[N][N];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};void bfs(){memset(dis,-1,sizeof dis);queue<Node> q;for(int i=0; i<n; i++)for(int j=0; j<m; j++)if(g[i][j] == '1')dis[i][j]=0, q.push({i,j});while(q.size()){auto t=q.front(); q.pop();for(int i=0; i < 4; i++){int a=t.x+dx[i], b=t.y+dy[i];if(a<0||a>=n||b<0||b>=m)continue;if(dis[a][b]!=-1) continue;dis[a][b]=dis[t.x][t.y]+1;q.push({a,b});}}
}