拖拉机
问题描述
干了一整天的活,农夫约翰完全忘记了他把拖拉机落在田地中央了。
他的奶牛非常调皮,决定对约翰来场恶作剧。
她们在田地的不同地方放了 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;
}