BFS——多源BFS、最小步数模型

文章目录

        • 多源BFS
          • 概述
          • 思路
          • 例题
        • 最小步数模型
          • 概述
          • 难点
          • 例题
        • 总结

多源BFS

概述

多源BFS,即同时存在多个起点,然后要求计算出图中任意一个点距离所有起点的最短距离,即图中任意点到每个起点的距离的最小值。

思路

一般单源BFS求最短路,是针对于单一起点来说计算图中其他点到起点的最短距离。多源BFS有多个起点,可以将所有起点看成一个整体,计算图中其他点到这个起点的最短距离。操作上只需要将单源BFS起始加入队列的单一起点,变为所有起点。

例题

给定一个 N 行 M 列的 01 矩阵 A,A[i][j] 与 A[k][l] 之间的曼哈顿距离定义为:

dist(A[i][j],A[k][l])=|i−k|+|j−l|
输出一个 N 行 M 列的整数矩阵 B,其中:

B[i][j]=min1≤x≤N,1≤y≤M,A[x][y]=1dist(A[i][j],A[x][y])
输入格式
第一行两个整数 N,M。

接下来一个 N 行 M 列的 01 矩阵,数字之间没有空格。

输出格式
一个 N 行 M 列的矩阵 B,相邻两个整数之间用一个空格隔开。

数据范围
1≤N,M≤1000
输入样例:
3 4
0001
0011
0110
输出样例:
3 2 1 0
2 1 0 0
1 0 0 1

from collections import deque
N = 1010
DIRC = [[-1, 0], [0, 1], [1, 0], [0, -1]]
g = []
dist = [[-1] * N for _ in range(N)]

def bfs() :
	que = deque()
	for i in range(n) :
		for j in range(m) :
			if g[i][j] == '1' : 
				que.appendleft([i, j])
				dist[i][j] = 0
	while len(que) != 0 :
		x, y = que.pop()
		for i in range(4) :
			a, b = x + DIRC[i][0], y + DIRC[i][1]
			if a < 0 or a >= n or b < 0 or b >= m : continue
			if dist[a][b] != -1 : continue
			que.appendleft([a, b])
			dist[a][b] = dist[x][y] + 1

n, m = map(int, input().split())
for i in range(n) :
	g.append(input())

bfs()
for i in range(n) :
	for j in range(m) :
		print(dist[i][j], end = " ")
	print()

最小步数模型

概述

BFS最短路模型,所有状态一般都是基于一个点向周围的点扩散的状态。每次只需要保存每次扩散遍历的点的状态即可,整个图本身不变。最小步数模型,每一步都会改变图的状态,此时我们需要保存的是图的状态。

难点
  1. 状态存储:一般使用一个字符串来存储每个状态
  2. 状态变化:状态变化将字符串映射到数组中进行转换,返回转换后的字符串
  3. 状态(距离)记录:通过哈希表记录转移到每个状态的距离
例题

Rubik 先生在发明了风靡全球的魔方之后,又发明了它的二维版本——魔板。

这是一张有 8 个大小相同的格子的魔板:

1 2 3 4
8 7 6 5
我们知道魔板的每一个方格都有一种颜色。

这 8 种颜色用前 8 个正整数来表示。

可以用颜色的序列来表示一种魔板状态,规定从魔板的左上角开始,沿顺时针方向依次取出整数,构成一个颜色序列。

对于上图的魔板状态,我们用序列 (1,2,3,4,5,6,7,8) 来表示,这是基本状态。

这里提供三种基本操作,分别用大写字母 A,B,C 来表示(可以通过这些操作改变魔板的状态):

A:交换上下两行;
B:将最右边的一列插入到最左边;
C:魔板中央对的4个数作顺时针旋转。

下面是对基本状态进行操作的示范:

A:

8 7 6 5
1 2 3 4
B:

4 1 2 3
5 8 7 6
C:

1 7 2 4
8 6 3 5
对于每种可能的状态,这三种基本操作都可以使用。

你要编程计算用最少的基本操作完成基本状态到特殊状态的转换,输出基本操作序列。

注意:数据保证一定有解。

输入格式
输入仅一行,包括 8 个整数,用空格分开,表示目标状态。

输出格式
输出文件的第一行包括一个整数,表示最短操作序列的长度。

如果操作序列的长度大于0,则在第二行输出字典序最小的操作序列。

数据范围
输入数据中的所有数字均为 1 到 8 之间的整数。

输入样例:
2 6 8 4 5 7 3 1
输出样例:
7
BCABCCB

from collections import deque

g = [[0] * 4 for _ in range(2)]
dist = dict()
pre = dict()

def set(state) :
	for i in range(4) : g[0][i] = state[i]
	j = 4
	for i in range(3, -1, -1) :
		g[1][i] = state[j]
		j += 1

def get() :
	res = ''
	for i in range(4) : res += g[0][i]
	for i in range(3, -1, -1) : res += g[1][i]
	return res

def move0(state) :#上下行变换
	set(state)
	for i in range(4) :
		g[0][i], g[1][i] = g[1][i], g[0][i]
	return get()

def move1(state) :#将最右边的一列插入到最左边
	set(state)
	v0, v1 = g[0][3], g[1][3]
	for i in range(3, 0, -1) :
		g[0][i], g[1][i] = g[0][i - 1], g[1][i - 1]
	g[0][0], g[1][0] = v0, v1
	return get()

def move2(state) : #魔板中央对的4个数作顺时针旋转
	set(state)
	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()

def bfs(st, ed) :
	if st == ed : return 0
	que = deque()
	que.appendleft(st)
	dist[st] = 0
	while len(que) :
		t = que.pop()
		m = []
		m.append(move0(t))
		m.append(move1(t))
		m.append(move2(t))
		
		for i in range(3) :
			if dist.get(m[i], -1) == -1 :
				que.appendleft(m[i])
				dist[m[i]] = dist[t] + 1
				pre[m[i]] = [chr(ord('A') + i), t]
		if m[i] == ed : return dist[ed]

ed = "".join(list(map(str, input().split())))
st = '12345678'

step = bfs(st, ed)

print(step)

res = ''
while ed != st :
	res += pre[ed][0]
	ed = pre[ed][1]
if step > 0 :
    print(res[:: -1])

总结

只要每一步都是分量相同,则可考虑BFS

你可能感兴趣的:(数据结构与算法,宽度优先,算法,图论)