算法 -- 03 -- DFS 与 BFS 综合学习(面向场景选择算法)

145 阅读2分钟

1.dfs为深度搜索算法

可以在脑海中想像一颗树, 从根节点开始选择左子节点或右子节点向下并且标记,直到到达叶子结点。 回溯到上一层,看是否存在未被标记的节点,然后向下走。

应用场景: 1.排列组合问题 2.N * N 走迷宫问题 3.连通块

实战分析:

1.排列问题

94. 递归实现排列型枚举 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 15;
int n;
int path[N];
bool st[N];
​
void dfs(int pos)
{
    if(pos > n) 
    {
        for(int i = 1; i <= n; i++)
        {
            cout<<path[i]<<" ";
        }
        cout<<endl;
        return ;
    }
    
    for(int i = 1; i <= n; i++)
    {
        if(!st[i])
        {
            path[pos] = i;
            st[i] = true;
            dfs(pos + 1);
            st[i] = false;//恢复现场
        }
    }
}
​
int main()
{
    cin>>n;
    dfs(1);
    return 0;
}

2.组合问题

92. 递归实现指数型枚举 - AcWing题库

分析问题:

题目可以抽象为从1~n中选数,存在一种选 / 不选的问题

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20;
int n;
int path[N];
bool st[N];
​
void dfs(int pos)
{
    if(pos > n) 
    {
        for(int i = 1; i <= n; i ++)
        {
            if(st[i]) cout<<i<<" ";
        }
        cout<<endl;
        return ;
    }
    
    //不选
    dfs(pos + 1);
    
    //选
    st[pos] = true;
    dfs(pos + 1);
    st[pos] = false; //恢复现场
}
​
int main()
{
    cin>>n;
    dfs(1); //个人习惯pos从1开始
    return 0;
}

3.走迷宫问题(是否可达/遍历)

1113. 红与黑 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30;
int n, m; //行列
char g[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
​
int dfs(int sx, int sy)
{
    
    int cnt = 1;
    g[sx][sy] = '#';
    for(int i = 0; i < 4; i++)
    {
        int x = sx + dx[i], y = sy + dy[i];
        if(x < 0 || x >= n || y < 0 || y >= m) continue;
        if(g[x][y] != '.') continue;
        cnt += dfs(x, y);
    }
    return cnt;
}
​
int main()
{
    while(cin>>m>>n, n || m)
    {
        for(int i = 0; i < n; i++) cin>>g[i];
        
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
            {
                if(g[i][j] == '@')
                    cout<<dfs(i, j)<<endl;
            }
​
    }
    return 0;
}

4.连通块(求连通块个数或大小)

1233. 全球变暖 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1000 + 10;
int n;
char g[N][N];
bool st[N][N];
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
​
void dfs(int sx, int sy, int &total, int &bound)
{
    st[sx][sy] = true;
    total ++;//连通块个数
    bool is_bound = false;
    for(int i = 0; i < 4; i++)
    {
        int x = sx + dx[i], y = sy + dy[i];
        if(x < 0 || x >= n || y < 0 || y >= n || st[x][y]) continue;
        if(g[x][y] == '.') //表示沿海
        {
            is_bound = true;
            continue;
        }
        dfs(x, y, total, bound);
    }
    if(is_bound) bound++;
    
}
​
int main()
{
    cin>>n;
    for(int i = 0; i < n; i++) cin>>g[i];
    int ans = 0;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
        {
            if(g[i][j] == '#' && !st[i][j])
            {
                int total = 0, bound = 0;
                dfs(i, j, total, bound);
                if(total == bound) ans ++;
            }
        }
    cout<<ans<<endl;
    return 0;
}

2.bfs为宽度搜索算法

可以在脑海中想像一棵树,从根节点开始选择下一层所有可达且未被标记的点加入队列中,一层一层的遍历下去,所以也称为层序遍历。

应用场景:

1.最短距离问题

844. 走迷宫 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 100 + 10;
int n, m;
int g[N][N];
int dist[N][N];
queue<PII> q;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
​
int bfs()
{
    memset(dist, -1, sizeof dist);
    q.push({0, 0});
    dist[0][0] = 0;
    
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        if(t.x == n - 1 && t.y == m - 1) return dist[n - 1][m - 1];
        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(g[a][b] == 1 || dist[a][b] != -1) continue;
            dist[a][b] = dist[t.x][t.y] + 1;
            q.push({a, b});
        }
    }
    return dist[n - 1][m - 1];
}
​
​
int main()
{
    cin>>n>>m;
    for(int i = 0; i < n; i++)
        for (int j = 0; j < m; j ++ )
            cin>>g[i][j];
    cout<<bfs()<<endl;
    return 0;
}
​
​

2.层序遍历

3.连通块

1113. 红与黑 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1000 + 10;
int n, m;
char g[N][N];
queue<PII> q;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
​
int bfs(int sx, int sy)
{
    q.push({sx, sy});
    g[sx][sy] = '#';
    int res = 0;
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        res ++;
        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(g[a][b] == '#') continue;
            q.push({a, b});
            g[a][b] = '#';
        }
    }
    return res;
}
​
int main()
{
    while(cin>>m>>n, n || m)
    {
        for(int i = 0; i < n; i++) cin>>g[i];
        
        PII start, end;
        for(int i = 0; i < n; i++)
            for(int j = 0; j < m; j++)
            {
                if(g[i][j] == '@')
                    cout<<bfs(i, j)<<endl;
            }
    }
    return 0;
}
​

两种搜索算法各有优劣,根据应用场景进行合适的选择

剪枝优化

两种搜索算法的一般适用于 n < 100左右的情况,所以有时会超时

于是需要剪去不满足条件的枝干

1.排列组合问题

93. 递归实现组合型枚举 - AcWing题库

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 30;
int n, m;
int path[N];
bool st[N];
​
void dfs(int pos, int start)
{
    //剪枝
    if(pos + n - start < m) return ;
    if(pos > m) 
    {
        for(int i = 1; i <= m; i++)
        {
            cout<<path[i]<<" ";
        }
        cout<<endl;
        return ;
    }
    
    for(int i = start; i <= n; i++)
    {
        if(!st[i])
        {
            path[pos] = i;
            st[i] = true;
            dfs(pos + 1, i);
            st[i] = false;
        }
    }
}
​
int main()
{
    cin>>n>>m;
    dfs(1, 1); // 位置,和选择的元素的起始位置
    return 0;
}

\