双端队列BFS

双端队列BFS

一、描述

双端队列 BFSBFSBFS 用于一张边权为 111000 的图。在这样的图上,可以使用双端队列来进行计算。算法的整体框架与一般的广搜相似,只是在在每个节点沿分支拓展时稍作改变。如果这条分支边权为 000 ,则从对首入队,否则从队尾入队。这样能保证任意时刻队列中的节点对应的距离之都有 “两端性” 与 “单调性” ;

二、效率分析

算法中每个节点仅被访问一次,因为这是 BFSBFSBFS 的特性,所以该算法的效率为 O(N)O(N)O(N)

三、正确性证明

由于我们最终目标是求路径权值和,而权值为 000 的边无论走多少条权值和依旧是 000 ,因此可以优先走权值为 000 的边,更新与这些边相连的点 xxxd[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;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值