蓝桥杯——搜索

搜索

DFS基础+回溯

回溯法简介:

回溯法一般使用DFS(深度优先搜索)实现,DFS是一种遍历或搜索图、树或图像等数据结构的算法,当然这个图、树未必要存储下来(隐式处理就是回溯法),常见的是通过某种关系构造出的搜索树,搜索树一般是排列型搜索树(总节点个数一般为n!级别)和子集型搜索树(总节点个数一般为2^n级别)。

排列型就是每次枚举选哪个,子集型就是对于每一个元素选或不选(结果与顺序无关)

DFS从起始节点开始,沿着一条路径尽可能深入地搜索(一条路走到黑),直到无法继续为止,然后回溯到前一个节点,继续探索其他路径,直到遍历完整个图或树。

DFS使用栈或递归来管理节点的遍历顺序,一般使用递归。

很多时候DFS和回溯法不必过度区分。

排列树
子集树

回溯法模板

这是一个排列型搜索树,实际上的回溯法比较灵活,需要根据题意要求来具体分析。

visll表示数字i是否使用过,也经常被用于表示某个元素是否使用过。

all存放结果,当dep深度=n + 1时说明n层都已经算完了,直接输出结果。

子集型搜索树模板结构类似,就是在往下走时候只有两条边,表示"选或不选当前这个元素"。

eg1------N皇后问题

代码:

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int a[11][11];
int ans = 0; int n;
bool check(int deep,int m){
    for(int k = 0; k < n ; ++ k){
        if(a[k][m]) return false;
    }
    // 检查所有方向以判断皇后是否会攻击
    //下方还没有放置皇后,所以不用检查
    for(int i = 1; i <= deep; i++) {
        if(a[deep - i][m]) return false; // 检查上方
        if(m - i >= 0 && a[deep - i][m - i]) return false; // 检查左上方
        if(m + i < n && a[deep - i][m + i]) return false; // 检查右上方
    }
    return true;
}
void dfs(int deep){
    if(deep == n){
        ans++;
        return;
    }
    for(int i = 0; i < n ; ++ i){
        if(check(deep,i)){
            a[deep][i] = 1; // 放置皇后
            dfs(deep+1);
            a[deep][i] = 0; // 移除皇后
        }
    }
}
int main(){
    cin>>n;
    dfs(0);
    cout<<ans;
    return 0;
}    
eg2------小朋友的崇拜圈
cpp 复制代码
#include<stdio.h>
int n;
int a[100001],v[100001];
int max=0;//定义全局变量,方便求最大值
int t;//暂时保存开始位置的小朋友的编号

void dfs(int x,int y)
{
  if(v[x])//只要访问过了已经访问的编号,就是有闭合的圈
  {
    if(a[x]==a[t])//只有满足这个条件,才符合一个真正意义上的圈
    {
      if(y>max)
        max=y;
    }
    return;
  }
  else
  {
    v[x]=1;
    dfs(a[x],y+1);
    v[x]=0;//每次进行一次dfs搜索,回溯,避免影响下一次的搜索
  }
}
int main()
{
  scanf("%d",&n);
  for(int i=1;i<=n;i++)
  scanf("%d",&a[i]);
  for(int i=1;i<=n;i++)//这个开始的编号要从1开始比较方便,因为每个人崇拜的小朋友的编号都是从下标为1开始的
  {
    t=i;//t暂时保存此时的为起点的小盆友的编号
    dfs(i,0);//每个小朋友都进行一次dfs搜索
  }
  printf("%d",max);
  return 0;
}
eg3------全球变暖
cpp 复制代码
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 1e3 + 10, M = N * N;

char g[N][N];
int n, ans, vis[N][N];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
bool flag = true;  // 假设被淹没

void has_submerge(int sx, int sy)
{
    vis[sx][sy] = 1;
    bool has_water = false;  // 假设周围没有水

    for(int i = 0; i < 4; i++)  // 遍历四个方向
    {
        int x = sx + dx[i], y = sy + dy[i];

        if(x < 0 || y < 0 || x >= n || y >= n) continue;

        if(vis[x][y] ) continue;

        if(g[x][y] == '.'){ has_water = true; continue;}

        vis[sx][sy] = 1;

        has_submerge(x, y);

    }
    if(!has_water) flag = false;  // 四个方向循环完再去更新flag 
}
int main()
{
    cin >> n;
    for(int i = 0; i < n; i++) cin >> g[i];

    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
         if(g[i][j] == '#' && !vis[i][j])
         {
             flag = true;
             has_submerge(i, j);
             if(flag) ans ++;
         }

    cout << ans << endl;
    return 0;
}
eg4------数字王国之军训排队
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=15;
int n;
int a[N];
vector<int> vec[N];//二维vector这样定义,别用vector<vector<int>> vec,因为前者外部vector已声明大小,可以直接调用迭代器,后者不行 
bool dfs(int dep,int i)//dep为第几个学生,i为分几队 
{//dfs用于判断是否可以分为i队 
    if(dep==n+1) return true;//如果递归到了最底层,说明可以分成i队 

    for(int j=0;j<i;j++)//将当前dep学生分到j队 
    {    bool flag=false;
        for(const auto &b:vec[j]) //遍历二维vector,遍历vector用auto枚举比较方便, 
        {
        //这种题型与组合枚举不一样,不要用两条件来思考 
            if(a[dep]%b==0) //如果当前将要入队的数字学生与队里面的数字可以整除,即有倍数关系 
            {                 //因为用sort排过序,所以a[dep]一定比b大 
                flag=true;break;
            }
             
            
        }
        if(flag) continue;
        vec[j].push_back(a[dep]);
        if(dfs(dep+1,i)) return true;//不要写成return true,布尔dfs的递归这样写,若为false仍需执行后面的恢复现场语句 
        //恢复现场 
        vec[j].pop_back();
    }
    return false; //中间执行完后没有return true,则return false 
}
int main()
{ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    sort(a+1,a+n+1);
     for(int i=1;i<=n;i++)//将1到n队都试试 
     {
         if(dfs(1,i)) //第一个可行的队即为最少的队 
         {
             cout<<i;
             break;
         }
     }
    return 0;
}
eg5------特殊的三角形
相关推荐
测试界的世清7 小时前
各大厂软件测试面试题+答案纯干货
面试·职场和发展
SunShaoLu8 小时前
实测7款思维导图工具图片导出能力:PNG/SVG/PDF全支持,附选型指南
职场和发展·效率工具·思维导图
闪电悠米9 小时前
力扣hot100-48.旋转图像-转置翻转详解
算法·leetcode·职场和发展
ychqsq19 小时前
95.归位
经验分享·职场和发展
小poop19 小时前
算法复杂度:从入门到面试必备
算法·面试·职场和发展
网安老伯1 天前
网络安全基础要点知识介绍(非常详细),零基础入门到精通,看这一篇就够了
运维·前端·网络协议·web安全·网络安全·职场和发展
叩码以求索1 天前
浅谈:算法萌新如何高效刷题应对面试(一)
算法·面试·职场和发展
谙弆悕博士1 天前
信息系统项目管理师教程(第4版)笔记——第 2 章 信息技术发展
笔记·职场和发展·软考高级·软考·高项·项目经理
wdfk_prog2 天前
嵌入式面试真题第 13 题:单硬件 Timer 下的高精度多路软件定时器架构设计
c语言·开发语言·面试·职场和发展·嵌入式
龙虾PRO2 天前
能源与股票量化核心差异,AI全链路落地实操手册
java·面试·职场和发展