2.2.1 DFS中的连通性和搜索顺序

DFS第一大类可以解决的问题

B F S / D F S ⇒ { F l o o d   F i l l 模 型 图 与 树 的 遍 历 BFS/ DFS\Rightarrow \left\{\begin{matrix} Flood\ Fill 模型\\ 图与树的遍历 \end{matrix}\right. BFS/DFS{ Flood Fill
此类问题,一般是判断树与图的内部,是否连通
DFS第一类问题

DFS第二类问题

搜索顺序,剪枝与优化,迭代加深, IDA*, 双向BFS。
这些问题基本是把整个问题看成是一个整体,问我们,能否从一个整体,转化为另外一个整体

把整体看成一个点,能否变化为另外一个点。在整体与整体之间做一个搜索。
dfs第二类问题

BFS与DFS的比较

BFS:不仅可以知道A与B是否连通,同时还可以知道当前A与B的距离是否是最短的距离
DFS: 只能求出来A与B是否连通, 第1次搜出来A和B, 只能知道A,B是否连通,并不能通过此来判断A与B之间的距离最短
DFS(优点): 代码较短

AcWing 1112. 迷宫

#include <iostream>
#include <cstring>
using namespace std;
const int N = 110;

char g[N][N];
int T;
int n;
bool st[N][N];
int dx[] = {
    
    -1, 0, 1, 0}, dy[] = {
    
    0, 1, 0, -1};
int xa, ya, xb, yb;

bool dfs(int x, int y){
    
    
    if (g[x][y] == '#') return false; //一定要写在开头,因为起点可能是#
    if (x == xb && y == yb) return true;
    st[x][y] = true;
    
    for (int i = 0; i < 4; i ++ ){
    
    
        int a = x + dx[i], b = y + dy[i];
        if (a < 0 || a >= n || b < 0 || b >= n) continue;

        if (st[a][b]) continue;
        if (dfs(a, b)) return true;
    }
    return false;
}

int main(){
    
    
    cin >> T;
    while (T -- ){
    
    
        memset(st, 0, sizeof st);
        cin >> n;
        for (int i = 0; i < n; i ++ ) cin >> g[i];
        cin >> xa >> ya >> xb >> yb;
        if (dfs(xa, ya)) puts("YES");
        else puts("NO");
    }
    return 0;
}

回溯问题(恢复现场问题)

26:30
对于第一类问题,要保证每个点只被搜索1次,是不能恢复现场的
对于第二类问题,把整个棋盘看成一个点,是必须恢复现场的,一定要把棋盘恢复原状,才能搜索下一个分支

AcWing 1113. 红与黑

#include <iostream>
#include <cstring>
using namespace std;
const int N = 30;
char g[N][N];
int m, n;
bool st[N][N];
int dx[] = {
    
    -1, 0, 1, 0}, dy[] = {
    
    0, 1, 0, -1};
int dfs(int x, int y){
    
    
    int cnt = 1;
    st[x][y] = true;
    
    for (int i = 0; i < 4; i ++ ){
    
    
        int a = x + dx[i], b = y + dy[i];
        if (a < 0 || a >= n || b < 0 || b >= m) continue;
        if (st[a][b]) continue;
        if (g[a][b] != '.') continue;
        cnt += dfs(a, b);
    }
    return cnt;
}

int main(){
    
    
    while (cin >> m >> n, m || n){
    
    
        memset(st, 0, sizeof st);
        for (int i = 0; i < n; i ++ ) cin >> g[i];
        int x, y;
        for (int i = 0; i < n; i ++ )
            for (int j = 0; j < m; j ++ )
                if (g[i][j] == '@') x = i, y = j;
        cout << dfs(x, y) << endl;
        
    }
    
    return 0;
}

AcWing 1116. 马走日

在这里插入图片描述

恢复现场写到dfs外面可以加快运行速度

#include <iostream>
using namespace std;
const int N = 15;
bool st[N][N];
int T, n, m, x, y;
int ans;
int dx[] = {
    
    -2, -1, 1, 2, 2, 1, -1, -2}, dy[] = {
    
    1, 2, 2, 1, -1, -2, -2, -1};
void dfs(int x, int y, int cnt){
    
    
    if (cnt == n * m){
    
    
        ans ++;
        return ;
    }
    
    st[x][y] = true;
    
    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 (st[a][b]) continue;
        dfs(a, b, cnt + 1);
    }
    
    st[x][y] = false;
}

int main(){
    
    
    cin >> T;
    while (T -- ){
    
    
        ans = 0;
        cin >> n >> m >> x >> y;
        
        dfs(x, y, 1);
        
        cout << ans << endl;
    }
    
    return 0;
}

AcWing 1117. 单词接龙

在这里插入图片描述

#include <iostream>
using namespace std;
const int N = 50;
int g[N][N]; //表示前一个单词与后一个单词最短重合距离,因为最短重合,才能使得龙最长
string word[N];
int n;
int used[N];
int ans;

void dfs(string dragon, int last){
    
    
    ans = max((int) dragon.size(), ans);
    
    used[last] ++;
    
    for (int i = 0; i < n; i ++ )
        if (g[last][i] && used[i] < 2)
            dfs(dragon + word[i].substr(g[last][i]), i);
    
    used[last] --;
}

int main(){
    
    
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> word[i];
    char start;
    cin >> start;
    
    for (int i = 0; i < n; i ++ )
        for (int j = 0; j < n; j ++ ){
    
    
            string a = word[i], b = word[j];
            for (int k = 1; k < min(a.size(), b.size()); k ++ )
                if (a.substr(a.size() - k, k) == b.substr(0, k)){
    
    
                    g[i][j] = k;
                    break;// 第1次搜到即为最短距离,因为长度是从小到大,直接break即可
                }
        }
    
    for (int i = 0; i < n; i ++ )
        if (word[i][0] == start)
            dfs(word[i], i);
    
    cout << ans << endl;
    
    return 0;
            
}

AcWing 1118. 分成互质组

在这里插入图片描述
图中递归搜索树,表示的含义:
组1(1) : 组1中放了下标是1的数。

需要考虑的问题:

优化

方案优化(搜索空间减小)

如果当前的数可以加到最后一个组,是否有必要新开一个组。
即:在图中1方案可选的情况下,另外选择2方案是否合理。
证明:
如果下标1(可以放到组3)的数开了组4, 然后组4后续又加了一些数,去掉1,组4中的所有数仍然互质,因此下标为1的数,可以放到组3。
这样做的结果,可以使得搜索空间减小

顺序优化(组合数的方式去搜索)

因为下标为1,2,3的数,以1,2,3次序, 2, 3,1次序加入到组中,与加入的顺序无关。
因此我们按照组合的方式去搜索
按照下标从小到大的情况加入组中,不会重复搜索。

参考orzorz大佬代码

#include <iostream>
#include <cstring>
using namespace std;

const int N = 11;

int n;
int p[N];
int group[N][N];
bool st[N];
int ans = N;

int gcd(int a, int b){
    
    
    return b ? gcd(b, a % b) : a;
}

bool check(int group[], int gc, int i){
    
    
    for (int j = 0; j < gc; j ++ )
        if (gcd(p[group[j]], p[i]) > 1)
            return false;
    return true;
}

void dfs(int g, int gc, int tc, int start){
    
    
    if (g >= ans) return ;
    if (tc == n) ans = g;
    
    bool flag = true;//从start开始找,是否有元素不能放到g组中
    for (int i = start; i < n; i ++ )
        if (!st[i] && check(group[g], gc, i)){
    
    
            st[i] = true;
            group[g][gc] = i; //group[g][gc] 用来记录当前g组gc位置放了原来数组中的哪个下标, 方便check函数
            dfs(g, gc + 1, tc + 1, i + 1);
            
            st[i] = false;
            flag = false;
        }
        
    //新开一个分组

    //由于dfs每层之间确定了顺序,所以期间是会有元素被漏掉的,【比如一开始你找的一串序列(1)是1,2,3,4 但是第二次(2)是1,3,4 很显然此时
    //(2)还有一个3没有得到分组,需要从start=0开始再把它找出来!  因此这样做仿佛有点浪费时间呢!!】

    //因此当所有元素都不能放进当前分组的时候 或者 当start=n-1了但是元素没有全部分组完毕时,要重新从start=0开始找,并且一定要有st数组!!!不然会把一个元素重复的分组!
    if (flag) dfs(g + 1, 0, tc, 0);
}

int main(){
    
    
    cin >> n;
    for (int i = 0; i < n; i ++ ) cin >> p[i];
    
    dfs(1, 0, 0, 0); //1.搜索第1组, 2.从1组的0号位置开始搜 3.当前一共搜索了0个元素 4.从p[i]数组的第0个位置搜
    
    cout << ans << endl;
    
    return 0;
}

猜你喜欢

转载自blog.csdn.net/esjiang/article/details/114056039