寒假每日一题——拖拉机

拖拉机

问题描述

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

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

她们在田地的不同地方放了 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

问题解决

1.证明双端队列广搜的队列中最多只有两段 一段为x 后一段为x+1
证明:

采用数学归纳法证明:
1.初始时,队列中只有起点,结论成立;
2.假设当k = n - 1时结论成立,证明当k = n时结论成立
3.因为当k = n - 1时成立,所以,x = x + 0放在队头, x = x + 1放在队尾,仍只有两段;若k = n - 1时只有一段,易证成立。故得证。

2.回归到题目

本题是点权问题,入队判重也可能对;若x,y在队列中,要到z的位置,而x到z是1,说明z是障碍物,那么y到z也是1

完整代码如下:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <deque>

using namespace std;

const int N = 1010;

bool g[N][N], st[N][N];
int d[N][N];
int n, sx, sy;
typedef pair<int, int> PII;


int bfs()//实际上是dijkstra算法
{
    deque<PII> dq;
    memset(d, 0x3f, sizeof d);
    
    dq.push_back({sx, sy});//将起点加入双端队列
    d[sx][sy] = 0;
    
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
    
    while(dq.size())
    {
        auto t = dq.front();
        dq.pop_front();
        
        int x = t.first, y = t.second;
        
        if(st[x][y]) continue;//表示该位置已经搜索过了
        st[x][y] = true;
        
        if(!x && !y)//即x y均等于0 表示拖拉机已开到原点
        break;
        
        
        for(int i = 0; i < 4; i++)
        {
            int nx = x + dx[i], ny = y + dy[i];
            if(nx >= 0 && nx < N && ny >= 0 && ny < N)
            {
                int w = 0;
                if(g[nx][ny]) w = 1;
                if(d[nx][ny] > d[x][y] + w)
                {
                    d[nx][ny] = d[x][y] + w;
                    if(w == 0) dq.push_front({nx, ny}); //w等于0加到队头
                    else dq.push_back({nx, ny});//w等于1加到队尾
                }
            }
        }
        
    }
    return d[0][0];
}

int main()
{
    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());
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值