题目
算法 双端队列广搜(本质是就是一个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;
}