[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的能力,其次就是审题要仔细一些,看清楚一些细节再写题。