搜索之DFS

DFS

DFS:深度优先搜索,不断向深层搜索,搜索至叶子节点后返回.

DFS基础应用:

  • DFS 连通性模型
  • DFS 搜索顺序
  • DFS 剪枝与优化
  • 迭代加深
  • 双向DFS
  • IDA*

关于是否回溯:

  • 对于图内的点的搜索,需要对每个点全部遍历一遍时,不需要回溯.
  • 对于图的不同状态进行搜索时,每搜索一个状态结束后,需要回溯恢复现场,以便对整个图下一个状态进行搜索.

DFS 连通性模型:

AcWing 1112. 迷宫

算法思路:

求解图中点的连通性,这里所有点需要遍历一遍,不需要回溯.

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

const int N = 110;
int k, sx,sy,ex,ey,n;

char g[N][N];
int dx[4] = {1,0,-1,0};
int dy[4] = {0,-1,0,1};
int st[N][N];

int dfs(int x, int y)
{
    if (g[x][y] == '#')
        return 0;
    if (x == ex && ey == y)
        return 1;
    st[x][y] = 1;
    
    for (int i = 0; i < 4; i ++ ){
        int xx = x + dx[i];
        int yy = y + dy[i];
        if (xx >= n || xx < 0 || yy >= n || yy < 0)
            continue;
        if (st[xx][yy])
            continue;
        if (dfs(xx,yy))
            return 1;
    }
    
    return 0;
}

int main()
{
    cin >> k;
    while (k -- ){
        cin >> n;
        char c = getchar();
        for (int i = 0; i < n; i ++ )
            scanf("%s",g[i]);
        memset(st, 0, sizeof st);
        cin >> sx >> sy >> ex >> ey;
        
        if (dfs(sx, sy))
            cout << "YES" << endl;
        else
            cout << "NO" << endl;
                
    }
    
    return 0;
}

AcWing 1113. 红与黑

算法思路 :

求连通块内点的数量,DFS不需要回溯.

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 30;

char g[N][N];
int st[N][N];
int n, m;
int dx[4] = {1,0,-1,0};
int dy[4] = {0,-1,0,1};

int dfs(int x, int y)
{
    int cnt = 1;
    
    st[x][y] = 1;
    
    for (int i = 0; i < 4; i ++ ){
        int xx = dx[i] + x;
        int yy = dy[i] + y;
        
        if (xx > n || xx < 1 || yy > m || yy < 1)
            continue;
        if (st[xx][yy])
            continue;
        if (g[xx][yy] != '.')
            continue;
        
        cnt += dfs(xx,yy);
    }
    
    return cnt;
}


int main()
{
    while (cin >> m >> n && n && m){
        char c = getchar();
        for (int i = 1; i <= n; i ++ )
            scanf("%s",g[i] + 1);
        int x, y;
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= m; j ++ )
                if (g[i][j] == '@'){
                    x = i;
                    y = j;
                }
        
        memset(st, 0, sizeof st);
        cout << dfs(x, y) << endl;
        
    }
    
    return 0;
}

DFS 搜索顺序

AcWing 1116. 马走日

算法思路 :

题目需要求解从起点出发有多少中方法可以走完全图, 对图的状态进行搜索, 每次向下搜索一次, 马走过的点多一个, 当下一步不再合法或是搜完全图后, 需要进行回溯恢复现场,以便对下一个状态继续搜索.

#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>

using namespace std;

const int N = 10;
int st[N][N];
int t;
int res;
int dx[8] = {-2, -1, 1, 2, 2, 1,-1,-2};
int dy[8] = { 1,  2, 2, 1,-1,-2,-2,-1};
int n,m;

void dfs(int x, int y, int cnt)
{
    if (cnt == n * m){
        res ++;
        return;
    }
    st[x][y] = 1;
    for (int i = 0; i < 8; i ++ ){
        int xx = x + dx[i];
        int yy = y + dy[i];
        
        if (xx < 0 || xx >= n || yy < 0 || yy >= m)
            continue;
        if (st[xx][yy])
            continue;
        
        
        dfs(xx, yy, cnt + 1);
        
    }
    st[x][y] = 0;
}

int main()
{
    cin >> t;
    while (t -- ){
        
        int x,y;
        cin >> n >> m >> x >> y;
        memset (st, 0, sizeof st);        
        res = 0;
        dfs(x,y,1);
        
        cout << res << endl;
    }
    
    return 0;
}

AcWing 1117. 单词接龙

算法思路 :

从起始单词开始向下搜索,每次遍历可以向后添加的单词.注意回溯.
注意求一个单词后缀与另一个单词的前缀的最小重合字母数量.

#include <iostream>
#include <cstring>
#include <queue>
#include <string>

using namespace std;

const int N = 25;
int n;
string words[N];
int g[N][N];        //g[i][j]表示i号单词与j号单词最小公共长度
int sum[N];         //存储每个单词出现的次数
int ans = 0;

void dfs(string state, int x)
{
    ans = max((int)state.size(), ans);
    //cout << state << endl;
    
    sum[x] ++;
    
    for (int i = 1; i <= n; i ++ )
        if (sum[i] < 2 && g[x][i]){
            dfs(state + words[i].substr(g[x][i]), i);
        }
    
    sum[x] --;
}


int main()
{
    cin >> n;
    for (int i = 1; i <= n; i ++ )
        cin >> words[i];
        
    char start;
    cin >> start;
    
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ ){
            for (int k = 1; k < words[i].size() && k < words[j].size(); k ++ )          //注意长度至少为1
                if (words[i].substr(words[i].size() - k, k) == words[j].substr(0,k)){
                    g[i][j] = k;
                    //cout << g[i][j] << ' ';
                    break;
                }
        }
    
    for (int i = 1; i <= n; i ++ )
        if (words[i][0] == start)
            dfs(words[i], i);
            
    cout << ans << endl;
    
    return 0;
}

AcWing 1118. 分成互质组

  • (待填)

DFS之剪枝与优化

常用剪枝方法:

  • 优化搜索顺序: 优先搜索分支较少的点

  • 排除等效冗余: 不搜索等效的状态(一般是将排列转化为组合)

  • 可行性剪枝: 不合法时及时停止

  • 最优性剪枝: 确定该方案不是最优解时及时停止

AcWing 165. 小猫爬山

算法思路:

这里对根据车的数量进行分支, 对每一只猫, 遍历每一辆车, 找到其中一辆可以加入的车, 当无法加入任何一辆车时, 新开一辆车, 直到所有猫都有车可坐,所以每只猫会有向下的\(k\)(当前车辆数) + 1 个分支
三点优化:

  1. 优化搜索顺序: 先加入胖猫,由于分支相同时,胖猫的合法分支较少.
  2. 最优性剪枝
  3. 可行性剪枝
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 20;

int cars[N];
int n, m;
int cats[N];
int ans = N;

void dfs(int u, int k)
{
    //最优性剪枝
    if (k >= ans)
        return;
    if (u > n){
        ans = k;
        return ;
    }
    for (int i = 1; i <= k; i ++ )
        if (cars[i] + cats[u] <= m){
            cars[i] += cats[u];
            
            dfs(u + 1, k);
            
            cars[i] -= cats[u];
        }
    cars[k + 1] = cats[u];
    dfs(u + 1, k + 1);
    cars[k + 1] = 0;
}

int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i ++ )
        cin >> cats[i];
        
    //优化搜索顺序:
    sort(cats + 1, cats + n + 1);
    reverse(cats + 1, cats + n + 1);
    dfs(1, 1);
    cout << ans << endl;
    return 0;
}
posted @ 2021-03-16 20:46  lhqwd  阅读(70)  评论(0)    收藏  举报