前言:
本系列是学习了董晓老师所讲的知识点做的笔记
董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (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;//如果遍历过 就continue
vis[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});
}
}
}