acwing寒假每日一题 2019.拖拉机 双端队列广搜

题目

在这里插入图片描述

算法 双端队列广搜(本质是就是一个dijkstra) 时间复杂度 n2

思路分析

可以把这个问题抽象为图,其中只有边权为0或者1的边,可以推广为任何只有两种边权,且边权相差为1的图。
双端队列广搜的证明:
1证明两段性:因为边权只有两种,所以一个点在更新dist的时候,最多有两种可能,就是+这两个边权其中之一,我们因为每次入队时只能入一个元素,所以当有n个元素的时候,一定是由n-1的元素入队而成的。且我们讲边权较小的进入队头,边权较大的进入队尾。保证了单调性和两端性,我们一直从队头取元素进行dist的更新,这样就实现了像dijkstra算法中小根堆取最小值的作用。

代码

#include<iostream>
#include<cstring>
#include<deque>
using namespace std;
const int N = 1010;
typedef pair<int, int> pii;
bool g[N][N]; //存地图的二维数组,讲问题抽象为图,如果有草堆赋值为1
int dist[N][N];//存从起点开始的最小距离
bool st[N][N];//存一个是否走过

int dijkstra(int sx, int sy)
{
    int dx[4] = { 0,0,-1,1 }; int dy[4] = { -1,1,0,0 };//向上下左右四个方向偏移
    deque<pii> q;//双端队列
    q.push_back({ sx,sy });//起点入队
    memset(dist, 0x3f, sizeof dist);//初始化距离为dist
    dist[sx][sy] = 0;
    while (q.size())
    {
        auto t = q.front();
        q.pop_front();
        if (st[t.first][t.second] == 1)  continue;//出队时的判重
        st[t.first][t.second] == 1;
        if (!t.first && !t.second) break;//如果走到了原点就退出循环
        for (int i = 0; i < 4; i++)//向上下左右四个方向走
        {
            int x = t.first + dx[i]; int y = t.second + dy[i];//走一步之后的坐标
            if (x >= 0 && x < N + 10 && y >= 0 && y < N + 10)//走之后坐标需要满足的条件
            {
                int w=0;//权重
                if (g[x][y] == 1) w = 1;
                if (dist[x][y] > dist[t.first][t.second] + w)//更新最短路
                {
                    dist[x][y] = dist[t.first][t.second] + w;
                    if (w == 1) q.push_back({ x,y });
                    else q.push_front({ x,y });
                }
            }
        }
    }
    return dist[0][0];
}
int main()
{
    int n, sx, sy;
    cin >> n >> sx >> sy;
    int tmp=n;
    while (n--)//草堆的读入
    {
        int a, b;
        cin >> a >> b;
        g[a][b] = 1;
    }
    cout << dijkstra(sx, sy)<<endl;
}

tips:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值