双端队列BFS
一、描述
双端队列 BFSBFSBFS 用于一张边权为 111 或 000 的图。在这样的图上,可以使用双端队列来进行计算。算法的整体框架与一般的广搜相似,只是在在每个节点沿分支拓展时稍作改变。如果这条分支边权为 000 ,则从对首入队,否则从队尾入队。这样能保证任意时刻队列中的节点对应的距离之都有 “两端性” 与 “单调性” ;
二、效率分析
算法中每个节点仅被访问一次,因为这是 BFSBFSBFS 的特性,所以该算法的效率为 O(N)O(N)O(N) ;
三、正确性证明
由于我们最终目标是求路径权值和,而权值为 000 的边无论走多少条权值和依旧是 000 ,因此可以优先走权值为 000 的边,更新与这些边相连的点 xxx 的 d[x]d[x]d[x] (从起点到 xxx 的最小权值和),此时 d[x]d[x]d[x] 一定是最小的,因为它是由尽可能多的权值为 000 的边更新而来。所以在队列中取出的节点同时满足 “连通性” 和 “权值最小” ,因此每个节点仅需被更新一次。
四、例题
拖拉机
题目
题目描述
农场上有N(1 <= N <= 50,000)堆草,放在不同的地点上。FJ有一辆拖拉机,也在农场上。拖拉机和草堆都表示为二维平面上的整数坐标,坐标值在1…1000的范围内。拖拉机的初始位置与所有草堆不同。
FJ开拖拉机时,只能平行于坐标轴(即东、南、西、北四个方向),而且每次开动的一段必须是整数长度。
例如,他可以向北开2个单位长度,然后向东开3个单位长度。拖拉机不能开到草堆的位置。
请帮助FJ计算出最少要移动多少个草堆,他才能将拖拉机开回坐标原点。
拖拉机可以开到1…1000之外的地方去。
输入格式
第1行: 3个整数,即N 和拖拉机的初始位置 (x,y)
第2…1+N行: 每行2个整数,表示一堆草的位置 (x,y)
输出格式
第1行: FJ必须移动的最少的草堆的数量
思路
由于此题只有有草堆与没有草堆两种状态,所以考虑双端队列 BFSBFSBFS ;
搜索扩展状态时将没有草堆的放队首,有草堆的放队尾即可;
当搜索到 (0,0)(0, 0)(0,0) 时,搜索结束;
代码
#include <cstdio>
#include <queue>
#include <algorithm>
#define MAXN 1015
using namespace std;
int n, sx, sy;
bool flag[MAXN][MAXN], f[MAXN][MAXN];
int wayx[4] = { 0, 0, 1, -1 };
int wayy[4] = { 1, -1, 0, 0 };
struct point {
int x, y, z;
} t;
int bfs(int sx, int sy, int ex, int ey) {
deque < point > q;
q.push_front( point ( { sx, sy, 0 } ) );
f[sx][sy] = true;
while (!q.empty()) {
t = q.front();
q.pop_front();
int xr = t.x, yr = t.y, zr = t.z;
if (xr == ex && yr == ey) {
return zr;
}
for (int i = 0; i < 4; i++) {
int dx = xr + wayx[i], dy = yr + wayy[i];
if (dx >= 0 && dx <= 1001 && dy >= 0 && dy <= 1001 && f[dx][dy] == false) {
f[dx][dy] = true;
if (flag[dx][dy] == true) {
q.push_back( point ( { dx, dy, zr + 1 } ) );
} else {
q.push_front( point ( { dx, dy, zr } ) );
}
}
}
}
return 0;
}
int main() {
scanf("%d %d %d", &n, &sx, &sy);
for (int i = 1; i <= n; i++) {
int x, y;
scanf("%d %d", &x, &y);
flag[x][y] = true;
}
printf("%d", bfs(sx, sy, 0, 0));
return 0;
}
649

被折叠的 条评论
为什么被折叠?



