面试题:BFS解决动态规划问题

887 阅读4分钟

1. BFS和动态规划

BFS,Breadth-First Search, 广度优先搜索,用于解决图遍历的算法,简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。

在这里插入图片描述

具体可以参看:图:广度优先遍历BFS和深度优先遍历DFS及python实现

动态规划,通常基于一个递推公式及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。具体参看动态规划算法

BFS和动态规划的联系

动态规划需要找出状态转移矩阵,有的时候状态转移方程很多找,比如背包问题的一些拓展题。与动态规划相比,BFS状态转移就比较容易容易找,从当前节点出发,如果下一个节点满足条件,那我就直接遍历下一个节点,很直观。

因此,尝试使用BFS来解决动态规划中的难题!

2. 方块问题

有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.

这里,如果没有且首尾两格也不同色这个条件,状态转移方程很容易写出:

dp[n]=2dp[n1]dp[n] = 2*dp[n-1]

简单解释下,dp[n]dp[n]代表第nn个方格涂法的方案数;由于当前这一格,只与上一格有关,只要不和上一格的颜色相同就可以了,那么当前格就有2种涂法,总共就是2dp[n1]2*dp[n-1]种涂法。

但是,这里加了个条件且首尾两格也不同色, 我们可能一时间没能写出状态转移方程。没关系,我们用BFS来解决。

BFS BFS进行转移也是当前节点的颜色和上一节点的颜色不一样就行。同时,考虑到且首尾两格也不同色, 因此,我们需要记录每个节点它的首格颜色,在第n1n-1格时,如果下一节点的颜色和它的首格颜色相同,则不能遍历那个节点。

先来看下代码:

from collections import deque
def bfs(n):
	queue = deque()
	# 3种状态转移
	color_list = ["red", "blue", "black"]
	# 开始节点的状态
	for cr in color_list:
		start = (cr, cr, 1)
		queue.append(start)
	method = 0
	while len(queue)>0:
		ct = len(queue)
		for _ in range(ct):
			color, head_color, x = queue.popleft()
			# 只要到达节点n,把涂法加1.
			if x == n:
				method +=1
				continue
			for cr in color_list:
				# 下一状态cr的颜色不能喝当前节点的颜色color一致
				if cr == color:
					continue
				else:
					# 在第(n-1)格,下一节点的颜色不能和初始的首格颜色一致。
					if (x == n-1) and (cr == head_color):
						continue
				queue.append((cr, head_color, x+1))
	return method

时间复杂度:O(nm)O(n*m), 其中, mm 是状态数; 空间复杂度:O(mn)O(m^n).

整个框架都是普通的BFS框架,只需要在里边添加转移条件就行了!!

话说回来,这题能否用动态规划去做呢?答案是肯定的。

在这里插入图片描述

代码:

def find_fill_method(n):
	# 
	dp = [0 for _ in range(n+1)]
	dp[0] = 0
	dp[1] = 3
	dp[2] = 6
	dp[3] = 6
	for i in range(4, n+1):
		dp[i] = dp[i-1] + 2*dp[i-2]
	return dp[n]

时间复杂度:O(n)O(n) 空间复杂度: O(n)O(n)

当然,这里的空间复杂度可以优化到O(1)O(1).

def find_fill_method2(n):
	if n == 0:
		return 0
	elif n == 1:
		return 3
	elif n == 2:
		return 6
	elif n == 3:
		return 6
	dp_n_2 = 6
	dp_n_1 = 6
	for i in range(4, n+1):
		#dp[i] = dp[i-1] + 2*dp[i-2]
		tmp = dp_n_1
		dp_n_1 = dp_n_1 + 2*dp_n_2
		dp_n_2 = tmp
		
	return dp_n_1

测试:

print(find_fill_method(10))
print(bfs(10))

结果:

1026
1026

3. 总结

当一些动态规划问题的状态转移方程很复杂时,不妨直接用BFS来做,状态转移只需要添加判断就行了,当然,由于BFS以模拟方式进行状态转移,因此,时间复杂度和空间复杂度都会更高。如果可以不考虑复杂度的情况下,BFS不亏是解决动态规划问题的绝佳模板!


参考:

  1. 有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.;
  2. 方块着色问题