1. BFS和动态规划
BFS,Breadth-First Search, 广度优先搜索,用于解决图遍历的算法,简单的说,BFS是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。
具体可以参看:图:广度优先遍历BFS和深度优先遍历DFS及python实现
动态规划,通常基于一个递推公式及一个或多个初始状态。 当前子问题的解将由上一次子问题的解推出。具体参看动态规划算法
BFS和动态规划的联系
动态规划需要找出状态转移矩阵,有的时候状态转移方程很多找,比如背包问题的一些拓展题。与动态规划相比,BFS状态转移就比较容易容易找,从当前节点出发,如果下一个节点满足条件,那我就直接遍历下一个节点,很直观。
因此,尝试使用BFS来解决动态规划中的难题!
2. 方块问题
有排成一行的n个方格,用红(Red)、粉(Pink)、绿(Green)三色涂每个格子,每格涂一色,要求任何相邻的方格不能同色,且首尾两格也不同色.求全部的满足要求的涂法.
这里,如果没有且首尾两格也不同色这个条件,状态转移方程很容易写出:
简单解释下,代表第个方格涂法的方案数;由于当前这一格,只与上一格有关,只要不和上一格的颜色相同就可以了,那么当前格就有2种涂法,总共就是种涂法。
但是,这里加了个条件且首尾两格也不同色, 我们可能一时间没能写出状态转移方程。没关系,我们用BFS来解决。
BFS BFS进行转移也是当前节点的颜色和上一节点的颜色不一样就行。同时,考虑到且首尾两格也不同色, 因此,我们需要记录每个节点它的首格颜色,在第格时,如果下一节点的颜色和它的首格颜色相同,则不能遍历那个节点。
先来看下代码:
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
时间复杂度:, 其中, 是状态数; 空间复杂度:.
整个框架都是普通的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]
时间复杂度: 空间复杂度:
当然,这里的空间复杂度可以优化到.
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不亏是解决动态规划问题的绝佳模板!
参考: