当前位置: 首页 > news >正文

算法第十二期——BFS-判重

目录

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) % 9
    t = list(status)
    t[pos], t[insertPos] = t[insertPos], t[pos]
    addStatus = "".join(t)
    if addStatus not in vis:
        vis.add(addStatus)      # 访问了就保存到vis
        q.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])
        break
    insertQueue(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) % 9
    t = list(status)
    t[pos], t[insertPos] = t[insertPos], t[pos]
    addStatus = "".join(t)
    if addStatus not in vis:
        vis.add(addStatus)      # 访问了就保存到vis
        q.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])
        break
    insertQueue(q, -2, news,vis)
    insertQueue(q, -1, news,vis)
    insertQueue(q, 1, news, vis)
    insertQueue(q, 2, news, vis)

相关文章:

  • 做黄色网站会受到什么惩罚/青岛seo关键字排名
  • 太原网站建设培训/曲靖seo
  • 口碑好的网站开发公司/网店推广策划方案
  • 做网站的域名和空间是什么意思/企业seo如何优化
  • 做外贸网站机构/关键词快速排名不限行业
  • 保定哪里有做网站的/51链
  • 机器学习实战4:基于马尔科夫随机场的图像分割(附Python代码)
  • 尚硅谷ES6李强笔记
  • 数据大佬的成长经验分享 | ​我的非典型数据分析之路
  • 使用动态输出打印内核的DEBUG信息
  • Vue3商店后台管理系统设计文稿篇(五)
  • 一次非典型的Netty内存泄露案例复盘
  • 测试开发——测试分类
  • 源码看CAF的线程调度框架
  • deap遗传算法 tirads代码解读
  • Himall商城ExpressDaDaHelper 取消订单(线上环境)
  • 【Linux系统】第四篇:Linux中编辑器vim的使用
  • 【深入浅出XML】包装纯粹信息的标记语言