算法第十二期——BFS-判重
创始人
2024-05-12 18:32:55

目录

BFS判重

Python判重方法: set、字典

set()判重

字典判重

 例题:跳蚱蜢

思路 

 【建模】

去重

代码一:字典去重(用list实现队列)

代码二:set()去重(用list实现队列)

代码二:set()去重(用deque实现队列) 推荐!


BFS判重

  • BFS=队列
  • BFS:逐步扩展下一层,把扩展出的下一层状态放进队列中处理。
  • 判重:若这些状态有相同的,只搜索一次,即进入队列一次,提高运行效率。

Python判重方法: set、字典

set()判重

  • set()函数创建一个无序、不重复元素集
  • 关系测试删除重复数据,计算交集、差集、并集、补集
# 对字符串去重
a = set ()
a.add("678"); print(a) # {'678'}
a.add("123"); print(a) # {'678', '123'}
a.add("678"); print(a) # {'678', '123'} 去重
b=sorted(a);  print(b) # ['123', '678'] sorted()返回的是列表
# 不能用sort(),因为sort()是list的方法# 对单个字符串去重是对每个字母去重
s=set("aabc"); print(s)# {'a', 'c', 'b'}
print(len(s))          # 3       有多少种不同的字母
print(sorted(s))       # ['a', 'b', 'c'] 排序
# 对多个字符串去重是对每个字符串去重
s=set(["aabc","bca","aabc"]); print(s) # {'aabc', 'bca'}# 对数字去重
b = set()
b.add(678) ; print(b)# {678}
b.add(123) ; print(b)# {123, 678}
b. add(678); print(b)# {123, 678}a=set([1,1,2,3,5]); print(a) # {1, 2, 3, 5}
b=set([1,2,3,4]);print(b)    # {1, 2, 3, 4}
print(a-b)  # 差集,在集合a 中但不在集合b 中的元素  # {5}
print(b-a)  # 差集                             # {4}
print(a&b)  # 交集,同时在集合a和b中的共同元素。    # {1, 2, 3}
print (a|b) # 并集,包括集合a和b中所有元素。       # {1, 2, 3, 4, 5}
print(a^b)  # 补集,包括集合a和b的非共同元素。      # {4, 5}

字典判重

  • 字典:无序、可变、有索引的集合。
  • 字典:用花括号定义,有键和值。
print(a^b)  # 补集,包括集合a和b的非共同元素。      # {4, 5}
#%%
a={5:"xy"}
# 没有这个键就添加,有这个键就修改(相当于去重)
a[1]="cb"   # 添加键值对
a[2]="kd"
a[2]="af"   # 修改键值对
print(a)  # {5: 'xy', 1: 'cb', 2: 'af'}
b=sorted(a.items()) ; print(b)   # 按键排序[(1, 'cb'), (2, 'af'), (5, 'xy')]
b=sorted(a.values()) ; print(b)  # 值排序 ['af', 'cb', 'xy']
b=sorted(a.items(),key=lambda x: x[0]); print(b)  # 按键排序  [(1, 'cb'), (2, 'af'), (5, 'xy')]
b=sorted(a.items(),key=lambda x: x[1]); print(b)  # 按值排序  [(2, 'af'), (1, 'cb'), (5, 'xy')]a={"food" :" xy" , " price" :555}
a[3]=899  # 键值对可以是不同类型
print(a)  # {'food': ' xy', ' price': 555, 3: 899}

 例题:跳蚱蜢

 跳蚱蜢2017年第八届省赛,lanqiao0J题号642

【题目描述】

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

如下图所示: 有 9 只盘子,排成 1 个圆圈。 其中 8 只盘子内装着 8 只蚱蜢,有一个是空盘。 我们把这些蚱蜢顺时针编号为 1 ~ 8。

每只蚱蜢都可以跳到相邻的空盘中, 也可以再用点力,越过一个相邻的蚱蜢跳到空盘中。

请你计算一下,如果要使得蚱蜢们的队形改为按照逆时针排列, 并且保持空盘的位置不变(也就是 1−8 换位,2−7换位,...),至少要经过多少次跳跃?

思路 

  • 从起始状态到终止状态,求最少跳跃次数
  • 最短路径问题
  • 用BFS

 【建模】

  • 直接让蚱蜢跳到空盘有点麻烦,因为有很多在跳蚱蜢。
  • 反过来看,让空盘跳,跳到蚱蜢的位置,简单多了,只有一个空盘在跳。

化圆为线

  • 题目是一个圆圈,不好处理,用一个建模技巧“化圆为线”,把圆形转换为线形。
  • 把空盘看成0,有9个数字{0,1,2,3,4,5,6,7,8},一个圆圈上的9个数字,拉直成了一条线上的9个数字,这条线的首尾两个数字0和8处理成相连的
  • 八数码问题:有9个数字{0,1,2,3,4,5,6,7,8},共有9!=362880种排列,不算多。

最短路径

  • 初始状态:“012345678”,目标状态:“087654321”。
  • 从初始状态“012345678”跳一次,有4种情况:0跳到1(向右跳一个),2(向右跳两个),7(向左跳一个),8(向左跳两个)这几个点,即“1023456783"、“210345678”、“812345670”、“712345608"。
  • 然后从这4种状态继续跳到下一种状态,一直跳到目标状态为止,第一个出现的‘’087654321就是最短路径。
  • 用BFS扩展每一层。
  • 每一层就是蚱蜢(建模为‘空盘’)跳了一次,扩展到某一层时发现终点“087654321”,这一层的深度就是蚱蜢跳跃的次数。

 第1步到第2步,有4种跳法;第2步到第3步,有4^2种;...;第20步,有4^20= 1万亿种,超时。

去重

判重:判断有没有重复跳,如果跳到一个曾经出现过的情况,就不用往下跳了。一共只有9!= 362880种情况。
代码复杂度:在每一层,能扩展出最少4种、最多362880种情况;若最后算出的答案是20层,那么最多算20*362880= 7,257,600次。在代码中统计实际的计算次数,是1451452次。
队列:最多有9!=362880种情况进入队列。

代码一:字典去重(用list实现队列)

  • list实现队列
  • 速度慢:3s,超时
def insertQueue(q: list, dir: int, news: tuple, vis):   #pos = news[1]               # 0的位置status = news[0]            # 当前状态insertPos = (pos + dir + 9) % 9  # 难点:化圆为线# 将字符串转为列表比较好处理t = list(status)t[pos], t[insertPos] = t[insertPos], t[pos]  # 空盘子与跳到的数交换# 处理完再转回字符串addStatus = "".join(t)# 去重if addStatus not in vis:    # 当前状态不在vis(没访问过)vis[addStatus] = 1      # 向字典vis添加,用去重作用q.append((addStatus, insertPos, news[2] + 1)) # 添加到队尾q = [("012345678",0,0)]         # 到当前状态,空盘子0的位置,步数
vis = {"012345678": 1}          # 访问情况,用于去重
while q:                        # 队列不为空news = q.pop(0)             # 弹出队头if news[0] == "087654321":  # 结束条件:到达了目标状态,输出最少步数print(news[2])          # news[2]三元组第三个:步数。break#扩展下一层的4种情况insertQueue(q, -2, news,vis)   # 左边跳一个insertQueue(q, -1, news,vis)   # 左边跳两个insertQueue(q, 1, news, vis)   # 右边跳一个insertQueue(q, 2, news, vis)   # 右边跳两个

代码二:set()去重(用list实现队列)

用list实现队列

这个方法比上面简单一点。

# 与其他代码不同的地方已用注释标出
def insertQueue(q: list, dir: int, news: tuple, vis):   pos = news[1]status = news[0]insertPos = (pos + dir + 9) % 9t = list(status)t[pos], t[insertPos] = t[insertPos], t[pos]addStatus = "".join(t)if addStatus not in vis:vis.add(addStatus)      # 访问了就保存到visq.append((addStatus, insertPos, news[2] + 1))q = [("012345678",0,0)]
vis = set()           # vis访问情况。用set()去重
vis.add("012345678")  # 初始化第一个状态,存到vis
while q:news = q.pop(0)if news[0] == "087654321":print(news[2])breakinsertQueue(q, -2, news,vis)insertQueue(q, -1, news,vis)insertQueue(q, 1, news, vis)insertQueue(q, 2, news, vis)

代码二:set()去重(用deque实现队列) 推荐!

 用deque实现队列

 速度快:1.4s

# 与其他代码不同的地方已用注释标出
from collections import *  # 需导入collections
def insertQueue(q: list, dir: int, news: tuple, vis):pos = news[1]status = news[0]insertPos = (pos + dir + 9) % 9t = list(status)t[pos], t[insertPos] = t[insertPos], t[pos]addStatus = "".join(t)if addStatus not in vis:vis.add(addStatus)      # 访问了就保存到visq.append((addStatus, insertPos, news[2] + 1))q = deque()            # 用deque去重
q.append(("012345678",0,0))  # 初始化队列
vis = set()            # vis访问情况
vis.add("012345678")   # 初始化第一个状态,存到vis
while q:news = q.popleft() # 弹出队头if news[0] == "087654321":print(news[2])breakinsertQueue(q, -2, news,vis)insertQueue(q, -1, news,vis)insertQueue(q, 1, news, vis)insertQueue(q, 2, news, vis)

相关内容

热门资讯

demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...