最短路模型
为什么BFS可以用来求权值为1的最短路?
由于我们知道Dijkstra算法肯定是正确的,那么我们可以通过证明bfs可以实现Dijkstra算法的功能,就证明了其正确性。
首先,我们知道Dijkstra算法的核心是每次从队列中取出一个最小的点,也就是小根堆的堆顶。由于在这里权值为1,我们可以证明dfs的普通队列有如下两个特点:
- 二段性:任意时刻队列中只有两种类型的点:{x,x ,x, ... , x + 1, x + 1, x + 1}
- 单调性,即x肯定在x + 1的前面
就可以得出,dfs的普通队列就相当于Dijkstra的小根堆,所以说我们每次从队列中取出的点,一定是队列中最小的点,所以说我们每次都使用权值最小的边来更新队列。
这里不予详细证明!因为上述两个特性都是容易得出,但要给出详细证明复杂且没有必要。
不要被打印路径问题吓到!
我们可以逆向建图正向输出
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n, m;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};
bool g[N][N];
PII pre[N][N];
void bfs()
{
memset(pre, -1, sizeof pre);
queue<PII> q;
q.push({n - 1, n - 1});
pre[n - 1][n - 1] = {n, n};
while(q.size())
{
auto t = q.front(); q.pop();
for(int i = 0; i < 4; i ++ )
{
int x = t.first + dx[i], y = t.second + dy[i];
if(x < 0 || y < 0 || x >= n || y >= n) continue;
if(g[x][y] == 1) continue;
if(pre[x][y].first != -1) continue;
pre[x][y] = {t.first, t.second};
q.push({x, y});
}
}
}
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ )
for(int j = 0; j < n; j ++ )
cin >> g[i][j];
bfs();
PII end(0, 0);
while(end.first != n)
{
cout << end.first << ' ' << end.second << endl;
end = pre[end.first][end.second];
}
return 0;
}
不蹩脚的象棋马
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1010;
int n, m;
char g[N][N];
int dist[N][N];
int dx[8] = {-1, -2, -2, -1, 1, 2, 2, 1};
int dy[8] = {-2, -1, 1, 2, 2, 1, -1, -2};
//The Knight 的位置用 K 来标记,障碍的位置用 * 来标记,草的位置用 H 来标记。
int bfs()
{
memset(dist, -1, sizeof dist);
queue<PII> q;
for(int i = 0; i < n; i ++ )
for(int j = 0; j < m; j ++ )
if(g[i][j] == 'K')
{
q.push({i, j});
dist[i][j] = 0;
break;
}
while(q.size())
{
auto t = q.front();
q.pop();
for(int i = 0; i < 8; i ++ )
{
int a = t.x + dx[i], b = t.y + dy[i];
if(a < 0 || a >= n || b < 0 || b >= m) continue;
if(g[a][b] == '*') continue;
if(dist[a][b] != -1) continue;
dist[a][b] = dist[t.x][t.y] + 1;
if(g[a][b] == 'H') return dist[a][b];
q.push({a, b});
}
}
return -1;
}
int main()
{
cin >> m >> n;
for(int i = 0; i < n; i ++ ) cin >> g[i];
cout << bfs() << endl;
return 0;
}
/*
从 X 移动到 X−1 或 X+1,每次移动花费一分钟
从 X 移动到 2∗X,每次移动花费一分钟
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <map>
using namespace std;
int n, k;
map<int, int> dist;
int change(int x, int i)
{
if(i == 0) return x - 1;
else if(i == 1) return x + 1;
else return x * 2;
}
int dfs()
{
if(n == k) return 0;//特判一下
queue<int> q;
q.push(n);
dist[n] = 0;
while(q.size())
{
int t = q.front(); q.pop();
for(int i = 0; i < 3; i ++ )
{
int tt = change(t, i);
if(dist.count(tt)) continue;//去重
dist[tt] = dist[t] + 1;
if(tt == k) return dist[tt];
if(tt < 0 || tt > 200010) continue;//肯定不是答案的数不需要假如队列了
q.push(tt);
}
}
}
int main()
{
cin >> n >> k;
cout << dfs() << endl;
return 0;
}
// 5->4=>8->16->17