最短路模型

为什么BFS可以用来求权值为1的最短路?

由于我们知道Dijkstra算法肯定是正确的,那么我们可以通过证明bfs可以实现Dijkstra算法的功能,就证明了其正确性。

首先,我们知道Dijkstra算法的核心是每次从队列中取出一个最小的点,也就是小根堆的堆顶。由于在这里权值为1,我们可以证明dfs的普通队列有如下两个特点:

  1. 二段性:任意时刻队列中只有两种类型的点:{x,x ,x, ... , x + 1, x + 1, x + 1}
  2. 单调性,即x肯定在x + 1的前面

就可以得出,dfs的普通队列就相当于Dijkstra的小根堆,所以说我们每次从队列中取出的点,一定是队列中最小的点,所以说我们每次都使用权值最小的边来更新队列。

这里不予详细证明!因为上述两个特性都是容易得出,但要给出详细证明复杂且没有必要。


1076. 迷宫问题 - AcWing题库

不要被打印路径问题吓到!

我们可以逆向建图正向输出 

#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;
}


188. 武士风度的牛 - AcWing题库

不蹩脚的象棋马

#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;
}


1100. 抓住那头牛 - AcWing题库

/*
从 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

posted @ 2022-05-05 08:41  光風霽月  阅读(14)  评论(0编辑  收藏  举报