蓝桥杯真题 青蛙跳杯子 Python (BFS搜状态求最小步数)

蓝桥杯真题 青蛙跳杯子 Python BFS搜状态

题目

X星球的流行宠物是青蛙,一般有两种颜色:白色和黑色。
X星球的居民喜欢把它们放在一排茶杯里,这样可以观察它们跳来跳去。
如下图,有一排杯子,左边的一个是空着的,右边的杯子,每个里边有一只青蛙。

*WWWBBB

其中,W字母表示白色青蛙,B表示黑色青蛙,*表示空杯子。

X星的青蛙很有些癖好,它们只做3个动作之一:

  1. 跳到相邻的空杯子里。
  2. 隔着1只其它的青蛙(随便什么颜色)跳到空杯子里。
  3. 隔着2只其它的青蛙(随便什么颜色)跳到空杯子里。

对于上图的局面,只要1步,就可跳成下图局面:

WWW*BBB

本题的任务就是已知初始局面,询问至少需要几步,才能跳成另一个目标局面。

输入为2行,2个串,表示初始局面和目标局面。
输出要求为一个整数,表示至少需要多少步的青蛙跳。

输入
输入为2行,2个串,表示初始局面和目标局面。
输出
输出要求为一个整数,表示至少需要多少步的青蛙跳。
样例输入
*WWBB
WWBB*
样例输出
2

分析

首先很明显求至少需要多少步 即 最短步数,容易想到bfs;
本题有WB两种青蛙会动,一定得分两种求?转换思维!青蛙不动让杯子“跳跃”!而跳跃可以理解为两个不同位置字符交换位置,这样就转化为了一维的bfs问题;

而bfs模板我们总结如下:

vis=[] / {} #记录搜索过的状态,{}可以绑定新属性,例如操作步数
def bfs()
	queue=[]#初始化队列,比如放入起点
	depth = 0 # 记录遍历到第几层(注意不同题目的转换)
	while len(queue)>0:#队列不为空
	    depth +=1#深度加1,扩散的次数
	    n = len(queue) #n为队列中的元素个数 
	    for i in range(n):#循环 n 次 循环队列中的节点:
	        node = queue.pop() #弹出该节点,比如坐标
	        for node 的所有相邻结点 m: #可以是走向
	            if m 未访问过 且满足边界:# 注意边界问题 judge函数
	                queue.append(m)
	                标记访问#比如记录距离,用来保证最短
	              #if 到达了什么情况,比如搜到了最终状态就可以退出(一维bfs)
	             
	print(d[n][m])#最终结果 这种是搜棋盘用的,(二维bfs)

所以大致思路就是从初始状态bfs搜索,一直搜索到end状态。为了统计到达end态的步数,我们对vis修改一下新增一个属性,选择字典,记录搜索过的状态:到达该状态的步数

还要注意这里的处理*跳跃方向的技巧

move=[-3,-2,-1,1,2,3]

另外为了保证最短步数,我们只记录搜到没有搜过的状态所用步数,这样就能保证最小;而当前搜到的状态的步数=上一状态的步数+1步

start=input()
end=input()
move=[-3,-2,-1,1,2,3]#对移动的处理
vis={start:0}#对vis数组增加一个属性记录到达(搜索到)该状态的步数
def bfs():
  global start,end,move,vis
  queue=[start]
  while queue:
    temp_state=queue.pop(0)
    idx = temp_state.index('*')
    for i in move:
      new_idx=idx+i
      if 0<=new_idx<len(start) and temp_state[new_idx] !='*':#judge判断条件好好想一想
        temp_list=list(temp_state)#把str转成list方便兑换位置,再转回来
        temp_list[idx],temp_list[new_idx] = temp_list[new_idx],temp_list[idx]
        next_state=''.join(temp_list)
        #print(next_state)
        if next_state not in vis:#如果没有搜过该状态记录一下,如果有搜过不做处理,我们只存放第一次搜到该状态的步数!
          vis[next_state] = vis[temp_state]+1#到达当前状态的步数=上一状态+1
        queue.append(next_state)
        if next_state==end:#搜到目标状态了
          print(vis[next_state])
          return
bfs()

但是在蓝桥上跑会超时。。。

思考哪里可以优化?

if next_state not in vis:#如果没有搜过该状态记录一下,如果有搜过不做处理,我们只存放第一次搜到该状态的步数!
          vis[next_state] = vis[temp_state]+1#到达当前状态的步数=上一状态+1

查看了不少的代码,发现改成以下就会神奇ac!

 if next_state not in vis:#看看是否已经搜索过,如果没有搜索过,加入vis,并记录从上一temp到达a状态的步数,每次是累加,到达temp的步数再加上1
          vis.setdefault(next_state, vis[temp_state] + 1)#{'*WWBB': 0, 'W*WBB': 1}

可能是因为setdefault函数效率高?学习了!以后dic赋值尽量用setdefault,如果键不存在于字典中,将会添加键并将值设为默认值。

dic.setdefault(key,value) 如果键不存在于字典中,将会添加键并将值设为默认值。只能添加一次! 如果键已经存在,不会进行改变

AC代码

反思总结

  • 思维转化,本题让空杯子动
  • BFS需要队列queue,pre记录前驱结点,searched记录访问过的结点,judge函数判断是否合法
  • 多打印中间过程帮助理解!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值