CF173B Chamber of Secrets
题外话
楼下好几人的题解与 oi-wiki 上同题的题解一样(没有恶意,勿喷)。不说啥回事,只是发现他们的代码部分有问题,包括题目翻译,虽然不影响结果,但是我还是想纠正一下。纠正请看本文最后。
题目大意:
光线从密室的左下角 (n,m) 的位置往左边射出来,要求我们最少对几个柱子施法才能把光线射到密室门口且光线的方向为左 (1,1)。
这里我用了双向队列优化 BFS,比一般的队列应该要快上不少。提交记录
解题思路:
用广搜从 n,m 开始遍历每个点,如果当前点是个空格,就依照当前方向把下一个状态 push
进队列里面。如果当前点是个柱子 (#) ,那么我们就讨论其他三个方向的光线,并分别 push
进队列。
定义:
$n\;m$:密室的大小
$check$:记忆化用的数组
$dx$ 数组和 $dy$ 数组:增量数组
$c$ 数组:密室
$q$:双端队列
$x\;y$:从队列中取出的坐标
$dir$:方向(数字 1、2、3、4 分别对应上下左右四个方向)
部分代码
push 部分代码(如果你用了结构体,就请忽略这部分代码):
//如果你用了结构体,就请忽略这个函数
void add_front(int x, int y, int dir, int cnt)
{
//direction翻译为方向的意思
if (cnt < check[x][y][dir])//如果当前插入的状态比之前的状态更优的话就插入,不然就拜拜
{
check[x][y][dir] = cnt;//更新记录数组
//这里可以用结构图代替push三个数,这样做只是因为我在OI-wiki上看到一种骚操作(见主函数的while循环)
q.push_front(dir);//虽然没用结构体,但是要保持提出的数据顺序得一致,我这里用的是「x坐标,y坐标,方向」的顺序
q.push_front(y);
q.push_front(x);
}
}
//如果你用了结构体,就请忽略这个函数
void add_back(int x, int y, int dir, int cnt)
{
//direction翻译为方向的意思
if (cnt < check[x][y][dir])
{
check[x][y][dir] = cnt;
//因为这里是从尾部插入的,要保持从头部取出的数的顺序一致,所以要改下push的顺序
q.push_back(x);
q.push_back(y);
q.push_back(dir);
}
}
BFS部分:
while (!q.empty())
{
int x = q[0], y = q[1], dir = q[2];//骚操作(如果会,请忽略这句话,反正我当初不会)
q.pop_front(), q.pop_front(), q.pop_front();
int cnt = check[x][y][dir];
int _x = x + dx[dir], _y = y + dy[dir];
if (_x >= 1 && _x <= n && _y >= 1 && _y <= m)//当坐标加上增量数组后依然在范围内,
{
add_front(_x, _y, dir, cnt);//往队列头部push当前方向
}
if (c[x][y] == '#')//如果当前位置有#号,讨论其他三个方向
{
for (int i = 1; i <= 4; i ++ )
{
if (i != dir) add_back(x, y, i, cnt + 1);
}
}
}
最后的温馨提示:
$check$ 数组记得初始化
$q$ 队列记得先 push
进最初位置
输出时记得特判!
纠正:
题意纠正:起点是在右下,终点是左上(虽然不影响结果)
代码纠正:终点光线的方向应该向左,起点光线方向应该还是向左