蓝桥杯——搜索

搜索

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------特殊的三角形
相关推荐
杰瑞学AI5 小时前
LeetCode详解之如何一步步优化到最佳解法:21. 合并两个有序链表
数据结构·python·算法·leetcode·链表·面试·职场和发展
石去皿5 小时前
力扣hot100 71-80记录
算法·leetcode·职场和发展
梭七y5 小时前
【力扣hot100题】(051)腐烂的橘子
算法·leetcode·职场和发展
梭七y7 小时前
【力扣hot100题】(071)每日温度
算法·leetcode·职场和发展
Allen Wurlitzer7 小时前
算法刷题记录——LeetCode篇(2.4) [第131~140题](持续更新)
算法·leetcode·职场和发展
ylfhpy7 小时前
Java面试黄金宝典35
java·数据库·sql·算法·面试·职场和发展
xuanjiong8 小时前
纯个人整理,蓝桥杯使用的算法模板day3(完全背包dp问题),手打个人理解注释,超全面,且均已验证成功(附带详细手写“模拟流程图”,全网首个
算法·蓝桥杯·流程图·动态规划
Dream it possible!11 小时前
LeetCode 热题 100_完全平方数(84_279_中等_C++)(动态规划(完全背包))
c++·leetcode·动态规划·完全背包
勤劳的进取家13 小时前
贪心算法的使用条件
数据结构·python·算法·贪心算法·排序算法·动态规划