[董晓算法]搜索相关题目及模板

  前言:

本系列是学习了董晓老师所讲的知识点做的笔记

董晓算法的个人空间-董晓算法个人主页-哔哩哔哩视频 (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});
    }
  }
}

DFS和BFS对比

  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值