基础搜索

基础搜索

深度优先搜索(dfs, 栈实现)

dfs十分简单,不搜到最后不会往回走

只需要一个例题就可以整明白dfs是怎么应用的

全排列问题

给定一个n,输出n的全排列

分析:

每增加一个数字就向下搜到一个节点

我们从第一个开始搜,直到搜到第n个数字结束

搜的过程中记录搜过了那个数字,之后就不能搜这个数字

代码实现:

#include <iostream>

using namespace std;

const int N = 10;

int n;
int val[N];
bool vis[N];

void dfs(int u)
{
    if (u == n)
    {
        for (int i = 0; i < n; i ++ ) printf("    %d", val[i]);
        printf("\n");
        return;
    }

    for (int i = 1; i <= n; i ++ )
        if (!vis[i])
        {
            val[u] = i;
            vis[i] = true;
            dfs(u + 1);
            // 恢复现场
            vis[i] = false;
            val[u] = 0;
        }
}

int main()
{
    scanf("%d", &n);

    dfs(0);

    return 0;
}

n皇后问题

[八皇后](P1219 [USACO1.5]八皇后 Checker Challenge - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

思路:

这是一个经典的dfs题

dfs的每个节点存的是每一层的棋子放到哪

还要额外开col, dg, udg这几个数组分别来存这一列,这个对角线,反对角线是否有棋子

代码实现:

#include <iostream>

using namespace std;

const int N = 30;

int n, tot;
int e[N];
bool col[N], dg[N], udg[N];

void dfs(int u)
{
    if (u == n)
    {
        tot ++ ;
        if (tot <= 3)
        {
            for (int i = 0; i < n; i ++ ) printf("%d ", e[i]);
            printf("\n");
        }
        return;
    }

    for (int i = 0; i < n; i ++ )
        if (!col[i] && !dg[u + i] && !udg[n - u + i])
        {
            e[u] = i + 1;
            col[i] = dg[u + i] = udg[n - u + i] = true;
            dfs(u + 1);
            col[i] = dg[u + i] = udg[n - u + i] = false;
        }
}

int main()
{
    scanf("%d", &n);

    dfs(0);
    printf("%d\n", tot);

    return 0;
}

第二种做法:

这种方法思路简单,每一个地方可以选择选或不选,分别递归

代码:

// 时间复杂度较高,会有几个点tle
#include <iostream>

using namespace std;

const int N = 30;

int n, cnt, tot;
int e[N];
bool col[N], row[N], dg[N], udg[N];

void dfs(int x, int y, int s)
{
    if (y == n) y = 0, x ++ ;
    if (x == n)
    {
        if (s == n && cnt < 3)
        {
            for (int i = 0; i < n; i ++ ) printf("%d ", e[i] + 1);
            printf("\n");
            cnt ++ ;
        }
        if (s == n) tot ++ ;
        return;
    }

    // 放棋子
    if (!row[x] && !col[y] && !dg[x + y] && !udg[x - y + n])
    {
        e[x] = y;
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = true;
        dfs(x, y + 1, s + 1);
        row[x] = col[y] = dg[x + y] = udg[x - y + n] = false;
        e[x] = 0;
    }

    // 不放棋子
    dfs(x, y + 1, s);
}

int main()
{
    scanf("%d", &n);

    dfs(0, 0, 0);
    printf("%d", tot);

    return 0;
}

宽度优先搜索(bfs, 队列实现)

宽度搜索比较沉稳,一层一层搜

因为bfs的搜索顺序,它具有最短路的性质,

如果有环的话,第一次搜到这个点的路径就是最短路

例题:

[马的遍历](P1443 马的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn))

bfs有一个技巧,搜下一个节点的时候不需要写很多个if

直接用一个向量来存

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

using namespace std;

typedef pair<int, int> PII;

const int N = 410;

int n, m, x, y;
vector<PII> q;
int d[N][N];

void bfs()
{
    memset(d, -1, sizeof d);
    d[x][y] = 0;
    q.push_back({x, y});
    int dx[8] = {1, 2, 2, 1, -1, -2, -2, -1};
    int dy[8] = {2, 1, -1, -2, 2, 1, -1, -2};
    int hh = 0, tt = 0;
    while (hh <= tt)
    {
        auto t = q[hh ++ ];
        for (int i = 0; i < 8; i ++ )
        {
            int a = t.first + dx[i], b = t.second + dy[i];
            if (a >= 1 && a <= n && b >= 1 && b <= m && d[a][b] == -1)
            {
                d[a][b] = d[t.first][t.second] + 1;
                q.push_back({a, b});
                tt ++ ;
            }
        }
    }
}

int main()
{
    scanf("%d%d%d%d", &n, &m, &x, &y);

    bfs();
    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ )
            printf("%-5d", d[i][j]);
        printf("\n");
    }

    return 0;
}
posted @ 2022-08-17 14:38  张詠然  阅读(26)  评论(0)    收藏  举报