双端队列算法 2019. 拖拉机

题目

干了一整天的活,农夫约翰完全忘记了他把拖拉机落在田地中央了。

他的奶牛非常调皮,决定对约翰来场恶作剧。

她们在田地的不同地方放了 N 捆干草,这样一来,约翰想要开走拖拉机就必须先移除一些干草捆。

拖拉机的位置以及 N 捆干草的位置都是二维平面上的整数坐标点。

拖拉机的初始位置上没有干草捆。

当约翰驾驶拖拉机时,他只能沿平行于坐标轴的方向(北,南,东和西)移动拖拉机,并且拖拉机必须每次移动整数距离。

例如,驾驶拖拉机先向北移动 2 单位长度,然后向东移动 3 单位长度。

拖拉机无法移动到干草捆占据的位置。

请帮助约翰确定他需要移除的干草捆的最小数量,以便他能够将拖拉机开到二维平面的原点。

输入格式
第一行包含三个整数:N 以及拖拉机的初始位置 (x,y)。
接下来 N 行,每行包含一个干草捆的位置坐标 (x,y)。

输出格式
输出约翰需要移除的干草捆的最小数量。

数据范围
1≤N≤50000,
1≤x,y≤1000

输入样例:

7 6 3
6 2
5 2
4 3
2 1
7 3
5 4
6 4

输出样例:

1

思路解析

读题 → 抽象题型为矩阵 → 转为常见模型——最短路径

此题用Dijkstra结合双端队列即可求解。

①任何时刻双端队列中所有点到原点的距离最多只有两端,且这两段只差1.
采用数学归纳法证明:
在这里插入图片描述
②题意可知:可走地图大小不止1000*1000,也可在其外围走,因为在设计算法时可指定其走的面积为1001*1001

实现方法:

  • 设置矩阵范围为 1002*1002 (1001*1001原则上也可以,但是由于计算问题,适当调大数字),即可满足矩阵上侧和右侧部分最外围一圈无障碍。
  • 矩阵遍历从0开始(其干草堆放置是从1开始的),即可满足矩阵下侧和左侧部分最外围一圈无障碍。

在这里插入图片描述

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#include<deque> //双端队列的头文件

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;
const int N = 1002;//定义矩阵边长

bool g[N][N];   //记录位置上是否有障碍物(有:true  无:flase)
bool st[N][N];  //判端是否走过此处(走过:true  无:false)
int dist[N][N];//最短路径距离

int bfs(int sx, int sy){
    
    
    
    deque<PII> q;//定义双端队列
    q.push_back({
    
    sx, sy});//起点加到双端队列
    memset(dist, 0x3f, sizeof dist);//把距离初始化成正无穷 
    dist[sx][sy] = 0;
    
    int dx[4] = {
    
    -1, 0, 1, 0}, dy[4] = {
    
    0, 1, 0, -1};//顺序:上右下左
     
    while(q.size()){
    
    //当队列不空
        auto t = q.front();//取出队头元素
        q.pop_front();//删除队头元素、
        
        if(st[t.x][t.y])continue;//判断当前点是否被搜过 
        st[t.x][t.y] = true;//判断当前点是否被搜过 
        
        if(!t.x && !t.y)break;//判断当前点是否被搜过 
        
        for(int i = 0; i < 4; i++){
    
    
            int x = t.x + dx[i], y = t.y + dy[i];//求当前方向坐标
            if(x >= 0 && x < N && y >= 0 &&y < N){
    
    
                int w = g[x][y]==1 ?1:0;//如果是障碍物,权值置为1 ,否则置为0
                //int w = 0;
                //if (g[x][y]) w = 1;
                if(dist[x][y] > dist[t.x][t.y] + w){
    
    //如果距离能被更新且比原有距离更短
                    dist[x][y] = dist[t.x][t.y] + w;//更新权值
                    if(!w) q.push_front({
    
    x,y});//加到队头
                    else q.push_back({
    
    x, y});//加到队尾
                }
            }
        }
    }
    
    return dist[0][0];
}


int main(){
    
    
    int n, sx, sy;//n是障碍物数量,sx,sy是起点
    scanf("%d%d%d", &n, &sx, &sy);
    while(n--){
    
    
        int x, y;
        scanf("%d%d", &x, &y);//读入障碍物的坐标
        g[x][y] = true;
    }
    printf("%d\n",bfs(sx,sy));//输出距离原点最短距离 
    
    return 0;
}

希望可以帮到你,欢迎点赞评论~


猜你喜欢

转载自blog.csdn.net/smile66688/article/details/122859175
今日推荐