题目训练(密码hpuacm):链接https://vjudge.net/contest/241948

DFS(深度优先搜索)是搜索手段之一。它从某个状态开始,不停的转移状态,知道无法转移,然后回退到前一步状态,继续转移到其他状态,如此不断重复,直到找到最终的解。

A题是标准的模板题,既可以用DFS写,也可以用上一篇博客讲的BFS写(链接戳此处)。下面给出两种写法

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 20+10;

int m ,n;
char str[MAXN][MAXN];
int vis[MAXN][MAXN];

int ans = 0;
int d[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
void DFS( int x, int y )
{
    if( vis[x][y] == 1 )
        return ;
    vis[x][y] = 1;
    ans ++;
    for( int i=0; i<4; i++ )
    {
        int dx = x + d[i][0];
        int dy = y + d[i][1];
        if( dx >= 0 && dx < n && dy >= 0 && dy < m && str[dx][dy] != '#' && vis[dx][dy] != 1 )
        {
            //cout << "here" << endl;
            DFS(dx, dy);
            //cout << "here2" <<endl;
        }

        //cout << "here" << endl;
        //vis[dx][dy];
    }
}

int main()
{
    while( scanf("%d %d", &m, &n ) != EOF, (m || n) )
    {
        ans = 0;
        memset(vis, 0, sizeof(vis));
        for( int i=0; i<n; i++ )
            scanf("%s", str[i] );
        int stx, sty;
        for( int i=0; i<n; i++ )
        {
            for( int j=0; j<m; j++ )
                if( str[i][j] == '@' )
                {
                    stx = i, sty = j;
                    break;
                }

        }
        DFS(stx, sty);
        printf("%d\n", ans );
    }





    return 0;
}
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 20 + 10;

int n, m;
char str[MAXN][MAXN];
int vis[MAXN][MAXN];
struct node{
    int x, y;
    int cnt = 0;
    friend bool operator < ( node a, node b )
    {
        return a.cnt < b.cnt;
    }

};

int ans = 0;
int d[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
void BFS( int x, int y )
{
    priority_queue<node> q;
    vis[x][y] = 1;
    node e1, e2;
    e1.x = x, e1.y = y;
    q.push(e1);
    while( !q.empty() )
    {
        e1 = q.top();
        //vis[e1.x][e1.y] = 1;
        q.pop();
        ans ++;
        for( int i=0; i<4; i++ )
        {
            e2.x = e1.x + d[i][0];
            e2.y = e1.y + d[i][1];
            if( e2.x >= 0 && e2.x < n && e2.y >= 0 && e2.y < m && str[e2.x][e2.y] != '#' && vis[e2.x][e2.y] != 1 )
            {
                q.push(e2);
                vis[e2.x][e2.y] = 1;
            }

        }


    }




}


int main()
{
    while( scanf("%d %d", &m, &n ), (m || n) )
    {
        ans = 0;
        memset(vis, 0, sizeof(vis));
        for( int i=0; i<n; i++ )
            scanf("%s", str[i] );
        int stx, sty;
        for( int i=0; i<n; i++ )
        {
            for( int j=0; j<m; j++ )
                if( str[i][j] == '@' )
                    stx = i, sty = j;
        }
        BFS(stx, sty);
        printf("%d\n", ans );

    }



    return 0;
}

B题是一个很不错的题,不仅用到了DFS同时还用到了奇偶剪枝,以及回溯

回溯就是从当前状态某一个方向进行下去,但是这个方向不能到达终点,于是就需要回溯到当前状态,从另一个方向搜索下去。其在DFS中使用的方法就是用一个数组标记已经访问过的节点,如果从这个节点进行下去的状态没有找到结果,就重新标记这个节点没有走过。

#include <bits/stdc++.h>
using namespace std;

int n, m, t;    //n行 m列 时间
int stx, sty;   // 起点
int edx, edy;   // 终点
char str[10][10];
int vis[10][10];
int d[4][2] = { 1,0, -1,0, 0,1, 0,-1 };
bool flag = false;      // 记录是否能找到一条这样的路

void DFS( int x, int y, int st )    // st为已经用的时间
{
    vis[x][y] = 1;
    if( str[x][y] == 'D' && st == t )
    {
        flag = true;
        return ;
    }
    /* 奇偶剪枝 */
    int temp = t - st - (abs(x - edx) + abs(y - edy));
    if( temp<0 || (temp&1))
        return ;
    for( int i=0; i<4; i++ )
    {
        int dx = x + d[i][0];
        int dy = y + d[i][1];
        if( dx >= 0 && dx < n && dy >= 0 && dy < m && str[dx][dy] != 'X' && vis[dx][dy] != 1 )
        {
            DFS(dx, dy, st+1);
            if( flag )  return ;    // 如果已经找到就终止
            vis[dx][dy] = 0;        // 没找到就回溯,从另一个方向继续寻找

        }
    }


}



int main()
{
    while( scanf("%d %d %d", &n, &m, &t) != EOF, m && n && t )
    {
        memset(vis, 0, sizeof(vis));
        flag = false;
        for( int i=0; i<n; i++ )
            scanf("%s", str[i]);
        for( int i=0; i<n; i++ )
            for( int j=0; j<m; j++ )
            {
                if( str[i][j] == 'S' )
                    stx = i, sty = j;
                if( str[i][j] == 'D' )
                    edx = i, edy = j;
            }




        DFS(stx, sty, 0);
        if( flag )
            printf("YES\n");
        else
            printf("NO\n");

    }



    return 0;
}

C题只说一下思路,先来一个for循环遍历整个图,遇到@就进入DFS将他和他相邻的@都变成 * 。最后用了几次DFS结果就是几。

G题的棋盘问题其技巧就是按行搜索,标记列

//#include <bits/stdc++.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using namespace std;
const int MAXN = 8+2;

int n, k;
char str[MAXN][MAXN];
int vis[MAXN];      // 按行搜索,所以只需要一位的标记数组,标记列

int d[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
int ans = 0;

void DFS( int x, int cnt )
{
    if( cnt == k )
    {
         ans ++;
         return ;  // 满足条件就可以删除当前的状态,因为再往下也不会有符合的状态了
    }

    for( int i=x; i<n; i++ )     // 按行搜索
    {
        for( int j=0; j<n; j++ )
        {
            if( str[i][j] == '#' && vis[j] != 1 )    // vis[j] 标记的是列,因为按行搜索不可能出现两个在同一行
            {
                vis[j] = 1;
                DFS(i+1, cnt+1);     // 每次递归都在原有的状态基础上增加了一个基于原有基础的状态节点
                                    // 所以不用考虑跳过某一行的问题,每遍历一行产生的新的状态数都是原有的
                                    // 可以单独存在的状态数乘上当前行的节点数
                vis[j] =0;     // 回溯,方便另一个状态的搜索
            }
        }
    }

}


int main()
{
    while( scanf("%d %d", &n, &k ), ( n != -1 || k != -1 ) )    // 都为-1的逆否是只要有一个不为-1
    {
        ans = 0;
        memset(vis, 0, sizeof(vis));
        for( int i=0; i<n; i++ )
            scanf("%s", str[i] );
        DFS(0,0);
        printf("%d\n", ans );

    }




    return 0;
}

I题遇到好几次,自己都没能写出来,其实就是一个数学+递归的题。

具体看一下这个博主的博客:https://blog.csdn.net/u012283461/article/details/52761238