洛谷 P1825 Corn Maze S 广度优先搜索 做题经历

[USACO11OPEN] Corn Maze S | P1825 BFS

这题主要就是坑点比较多,写代码并不复杂,算法也不复杂。

首先一看题目DFS(深度优先搜索)好像可以,因为DFS就是模拟奶牛往下走的过程嘛,也不难写然后就有了第一版的代码:

#include <bits/stdc++.h>
using namespace std;
int N, M;
struct point
{
    int i, j;
};
char place[301][301];
map<char, list<struct point> > transit;
bool path[301][301];
int min_time = 999999;
int start_i, start_j, end_i, end_j;
bool dfs(int count, int i, int j)
{
    // cout << count << ' ' << place[i][j] << ' ' << i << ' ' << j << endl;
    if (place[i][j] == '#' || path[i][j])
    {
        return false;
    }
    if (i == end_i && j == end_j)
    {
        min_time = min(count, min_time);
        return true;
    }
    path[i][j] = true;
    if (place[i][j] >= 'A' && place[i][j] <= 'Z')
    {

        list<struct point>::iterator it1 = transit[place[i][j]].begin();
        for (; it1 != transit[place[i][j]].end(); it1++)
        {
            if ((*it1).i != i && (*it1).j != j)
                dfs(count, (*it1).i, (*it1).j);
        }
    }
    dfs(count + 1, i, j - 1);
    dfs(count + 1, i, j + 1);
    dfs(count + 1, i - 1, j);
    dfs(count + 1, i + 1, j);
    path[i][j] = false;
    return false;
}
int main()
{
    cin >> N >> M;
    for (int i = 1; i <= N; i++)
    {
        for (int j = 1; j <= M; j++)
        {
            cin >> place[i][j];
            if (place[i][j] == '@')
            {
                start_i = i;
                start_j = j;
            }
            if (place[i][j] == '=')
            {
                end_i = i;
                end_j = j;
            }
            if (place[i][j] >= 'A' && place[i][j] <= 'Z')
            {
                struct point p = {i, j};
                transit[place[i][j]].insert(transit[place[i][j]].end(), p);
            }
        }
    }
    dfs(0, start_i, start_j);
    cout << min_time;
    return 0;
}

然后就只拿了12分,奇怪了,为什么会这样,明明就是按照题目写的,还有一堆TLE的,索性先不管TLE的先把WA的处理了。
在这里插入图片描述
然后发现了第一个bug

            if ((*it1).i != i && (*it1).j != j)
                dfs(count, (*it1).i, (*it1).j);

这里的判断条件写错了,不应该是两个都不相同才转移,(为啥经常犯这种低级错误呢?还特别难找)

改过之后

            if ((*it1).i != i || (*it1).j != j)
                dfs(count, (*it1).i, (*it1).j);

然后结果
在这里插入图片描述

为什么会这样呢?

再去审题就会发现一个想当然的地方

如果一头奶牛处在这个装置的起点或者终点,这头奶牛就必须使用这个装置。

还以为还是可以选择传送或者转移呢,原来是必须要传送啊

此时我又陷入了迷茫,必须要转移的话那是不是会导致一直循环转移啊?还是只能转移一次吗?

然后我无奈之下看了题解

为啥全是BFS(广度优先搜索)啊??肯定是互相抄的结果,我偏不信邪,就要用DFS来写。

不过还是有收获的可以直接让那个转移,在本层转移

bool dfs(int count, int i, int j)
{
    if (place[i][j] == '=')
    {
        min_time = min(count, min_time);
        return true;
    }
    if (i > N || i < 1 || j > M || j < 1 || path[i][j] || place[i][j] == '#')
    {
        return false; // 不满足条件直接回溯
    }
    path[i][j] = true; // 初始化条件
    if (place[i][j] >= 'A' && place[i][j] <= 'Z')
    {
        list<struct point>::iterator it1 = transit[place[i][j]].begin();
        for (; it1 != transit[place[i][j]].end(); it1++)
        {
            if ((*it1).i != i || (*it1).j != j)
            {
                i = (*it1).i;
                j = (*it1).j;
                break;
            }
        }
    }
    dfs(count + 1, i + 1, j );
    dfs(count + 1, i - 1, j);
    dfs(count + 1, i, j - 1);
    dfs(count + 1, i, j + 1);
    path[i][j] = false; // 恢复状态
    return false;
}

这样不就好了

结果
在这里插入图片描述

为啥还是这样啊?还有WA的啊?

然后我又左思右想,是不是初始化的点不太对啊?回复状态的点也不太对啊?

确实是这样的,我们转移状态后恢复的状态应该是最一开始的点的状态,而不是那个传送点的状态。

至于为什么传送后的点的状态不用的初始化和恢复,因为这个点后边还可以转回来使用

bool dfs(int count, int i, int j)
{
    int init_i = i, init_j = j;
    if (place[i][j] == '=')
    {
        min_time = min(count, min_time);
        return true;
    }
    if (i > N || i < 1 || j > M || j < 1 || path[i][j] || place[i][j] == '#')
    {
        return false; // 不满足条件直接回溯
    }
    if (place[i][j] >= 'A' && place[i][j] <= 'Z')
    {
        list<struct point>::iterator it1 = transit[place[i][j]].begin();
        for (; it1 != transit[place[i][j]].end(); it1++)
        {
            if ((*it1).i != i || (*it1).j != j)
            {
                i = (*it1).i;
                j = (*it1).j;
                break;
            }
        }
    }
    path[init_i][init_j]=true;  
    if (i + 1 >= 1 && i + 1 <= N && j >= 1 && j <= M && !path[i + 1][j] && place[i + 1][j] != '#')
        dfs(count + 1, i + 1, j);
    if (i - 1 >= 1 && i - 1 <= N && j >= 1 && j <= M && !path[i - 1][j] && place[i - 1][j] != '#')
        dfs(count + 1, i - 1, j);
    if (i >= 1 && i <= N && j - 1 >= 1 && j - 1 <= M && !path[i][j - 1] && place[i][j - 1] != '#')
        dfs(count + 1, i, j - 1);
    if (i >= 1 && i <= N && j + 1 >= 1 && j + 1 <= M && !path[i][j + 1] && place[i][j + 1] != '#')
        dfs(count + 1, i, j + 1);
    path[init_i][init_j] = false; // 恢复状态
    return false;
}

我顺便还剪了剪枝

结果

在这里插入图片描述

唉无能为力的,只能放弃DFS了,而且再剪枝也减不了了,因为DFS进行了许多重复运算,要是

我会记忆深度优先搜索就好了,那样减少了重复运算没准就可以了。

然后只能转战BFS了

一不小心又出了个错 就是第47行多写的一个等于号,导致只对了37分,改过来后下全对

#include <bits/stdc++.h>
using namespace std;
int N, M;
struct point
{
    int i, j, c;
};
char place[301][301];
map<char, list<struct point> > transit;
bool path[301][301];
int min_time = 1 << 20;
int temp[4] = {1, 0, -1, 0};
int temp2[4] = {0, 1, 0, -1};
int start_i, start_j;
queue<point> q;

int bfs()
{
    q.push((struct point){start_i, start_j});
    struct point p;
    while (!q.empty())
    {
        p = q.front();
        q.pop();
        if (place[p.i][p.j] == '=')
        {
            return p.c;
        }
        if (place[p.i][p.j] >= 'A' && place[p.i][p.j] <= 'Z')
        {
            list<struct point>::iterator it1 = transit[place[p.i][p.j]].begin();
            for (; it1 != transit[place[p.i][p.j]].end(); it1++)
            {
                if ((*it1).i != p.i || (*it1).j != p.j)
                {
                    p.i = (*it1).i;
                    p.j = (*it1).j;
                    break;
                }
            }
        }
        for (int i = 0; i < 4; i++)
        {
            int x = p.i + temp[i], y = p.j + temp2[i];
            if (place[x][y] != '#' && !path[x][y] && x >= 1 && x <= N && y >= 1 && y <= M)
            {
                path[x][y] = true;
                q.push((point){x, y, p.c + 1});
            }
        }
    }
    return -1;
}
int main()
{
    cin >> N >> M;
    for (int i = 1; i <= N; i++)
    {
        for (int j = 1; j <= M; j++)
        {
            cin >> place[i][j];
            if (place[i][j] == '@')
            {
                start_i = i;
                start_j = j;
            }
            else if (place[i][j] >= 'A' && place[i][j] <= 'Z')
            {
                struct point p = {i, j, 0};
                transit[place[i][j]].push_back(p);
            }
        }
    }
    cout << bfs();
    return 0;
}

结果:

在这里插入图片描述

总结

这题不难考的就是审题和BFS的书写。

我主要就是老卡在一些审题和小细节的bug 上什么时候我才能养成不出小bug 的能力,和快速检查出bug的能力,其次就是审题要仔细一些,看清楚一些细节再写题。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值