算法提高之搜索:多源BFS、最小步数模型、双端队列广搜

这篇博客介绍了三种图算法的应用:1.使用BFS计算矩阵中1的最短距离;2.解决魔板问题,通过BFS找到变换步数;3.利用双端队列的Dijkstra算法解决电路维修问题,寻找最小转换步骤。内容涵盖了数据结构与算法在实际问题中的应用。
摘要由CSDN通过智能技术生成

1、矩阵距离

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <cstring>
#include <iostream>
#include <algorithm>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 1010, M = N * N;

int n, m;
char g[N][N];
PII q[M];
int dist[N][N];

void bfs()
{
    int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1};

    memset(dist, -1, sizeof dist);

    int hh = 0, tt = -1;
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            if (g[i][j] == '1')
            {
                dist[i][j] = 0;
                q[ ++ tt] = {i, j};
            }

    while (hh <= tt)
    {
        auto t = q[hh ++ ];

        for (int i = 0; i < 4; i ++ )
        {
            int a = t.x + dx[i], b = t.y + dy[i];
            if (a < 1 || a > n || b < 1 || b > m) continue;
            if (dist[a][b] != -1) continue;

            dist[a][b] = dist[t.x][t.y] + 1;
            q[ ++ tt] = {a, b};
        }
    }
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%s", g[i] + 1);

    bfs();

    for (int i = 1; i <= n; i ++ )
    {
        for (int j = 1; j <= m; j ++ ) printf("%d ", dist[i][j]);
        puts("");
    }

    return 0;
}

2、魔板

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <queue>

using namespace std;

//涉及到get和set 使用char方便操作
char g[2][4];
unordered_map<string, pair<char, string>> pre;
unordered_map<string, int> dist;

void set(string state) 
{
    for (int i = 0; i < 4; i ++ ) g[0][i] = state[i];
    for (int i = 7, j = 0; j < 4; i --, j ++ ) g[1][j] = state[i];
}

string get()
{
    string res;
    for (int i = 0; i < 4; i ++ ) res += g[0][i];
    for (int i = 3; i >= 0; i -- ) res += g[1][i];
    return res;
}

string move0(string state)
{
    set(state);
    for (int i = 0; i < 4; i ++ ) swap(g[0][i], g[1][i]);
    return get();
}
 
string move1(string state)
{
    set(state);
    int v0 = g[0][3], v1 = g[1][3];
    for (int i = 3; i >= 0; i -- )
    {
        g[0][i] = g[0][i - 1];
        g[1][i] = g[1][i - 1];
    }
    g[0][0] = v0, g[1][0] = v1;
    return get();
}

string move2(string state)
{
    set(state);
    char v = g[0][1];
    g[0][1] = g[1][1];
    g[1][1] = g[1][2];
    g[1][2] = g[0][2];
    g[0][2] = v;
    return get();
}

int bfs(string start, string end)
{
    if (start == end) return 0;

    queue<string> q;
    q.push(start);
    dist[start] = 0;

    while (!q.empty())
    {
        auto t = q.front();
        q.pop();

        string m[3];
        m[0] = move0(t);
        m[1] = move1(t);
        m[2] = move2(t);

        for (int i = 0; i < 3; i ++ )
            if (!dist.count(m[i]))
            {
                dist[m[i]] = dist[t] + 1;
                pre[m[i]] = {'A' + i, t};
                q.push(m[i]);
                if (m[i] == end) return dist[end];
            }
    }

    return -1;
}

int main()
{
    int x;
    string start, end;
    for (int i = 0; i < 8; i ++ )
    {
        cin >> x;
        end += char(x + '0');
    }

    for (int i = 1; i <= 8; i ++ ) start += char('0' + i);

    int step = bfs(start, end);

    cout << step << endl;

    string res;
    while (end != start)
    {
        res += pre[end].first;
        end = pre[end].second;
    }

    reverse(res.begin(), res.end());

    if (step > 0) cout << res << endl;

    return 0;
}

3、电路维修

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
dijkstra可以做
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
点的坐标
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
字符的坐标
在这里插入图片描述
在这里插入图片描述
int bfs()
{
deque q;
}
在这里插入图片描述
在这里插入图片描述

/*
双端对列广搜:能走通的设置为0,走不通需要转化的设置为1.形成一个边权为0和1的无向图
当遍历到0时,将0放到队首。遍历到1时,将1放到队尾。
本问题可以视为使用双端队列解决特殊的dijstra问题。不是bfs问题。
一般bfs问题,所有的点只能遍历一次,在入队时,确定最小值。
对于本问题是,所有的点可以被遍历很多次,在出队时,决定最小值。
*/
#include <cstring>
#include <iostream>
#include <algorithm>
#include <deque>

#define x first
#define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 510, M = N * N;

int n, m;
char g[N][N];
//因为是dijsktra算法
//需要距离 和判重数组
int dist[N][N];
//判重数组是,防止使用一个点多次更新到其它点的距离
//模仿dijsktra
bool st[N][N];

int bfs()
{
    memset(dist, 0x3f, sizeof dist);
    memset(st, 0, sizeof st);
    dist[0][0] = 0;
    deque<PII> q;
    q.push_back({0, 0});
 	//按字符坐标 得到格子中的字符
 	// \\转义字符 需要打2个
    char cs[] = "\\/\\/";
    //周围的点
    int dx[4] = {-1, -1, 1, 1}, dy[4] = {-1, 1, 1, -1};
    //周围格子中的字符坐标
    int ix[4] = {-1, -1, 0, 0}, iy[4] =  {-1, 0, 0, -1};

    while (q.size())
    {
        PII t = q.front();
        q.pop_front();
		//模仿在dijsktra中 扩展一个点到周围点的最小距离
		//出队时,这个点的dist是最小值
        if (st[t.x][t.y]) continue;
        st[t.x][t.y] = true;

        for (int i = 0; i < 4; i ++ )
        {
            int a = t.x + dx[i], b = t.y + dy[i];
            //格点的长度比格子的长度多1.
            //格子的长度是(0,0)-(n-1,m-1)
            //格点是(0,0)-(n,m)
            if (a < 0 || a > n || b < 0 || b > m) continue;
			//格子中字符
            int ca = t. x + ix[i], cb = t.y + iy[i];
   //g[ca][cb]真实格子中的字符 cs[i] 以t这个点向a,b走,需要格子字符的走向
   //相同 不加,不同则+1
            int d = dist[t.x][t.y] + (g[ca][cb] != cs[i]);

            if (d <dist[a][b])
            {
                dist[a][b] = d;
				//边权为1 插入到队尾
                if (g[ca][cb] != cs[i]) q.push_back({a, b});
                //边权为0 插入到队头
                else q.push_front({a, b});
            }
        }
    }
	
    return dist[n][m];
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T -- )
    {
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i ++ ) scanf("%s", g[i]);

        int t = bfs();

        if (t == 0x3f3f3f3f) puts("NO SOLUTION");
        else printf("%d\n", t);
    } 

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值