Python真题练习3(单源、多源BFS总结)

参考文章:洛谷 P2678 & [NOIP2015提高组] 跳石头 - 技术经验 - W3xue【蓝桥杯真题】16天冲刺 Python_Py小郑的博客-CSDN博客

1.跳石头

L,N,M=map(int,input().split())
distances=[int(input()) for _ in range(N)]
distances.append(L)

#题目要求找出最短跳跃距离的最大值,
#首先给出一个跳跃距离,我们的判断其是否为最短跳跃距离,
#换个思路,我们假设当前所给的跳跃距离就是所有距离中最短的,
#我们只需判断其是否合法,即我们只有 M 次移走岩石的机会使得剩下的跳跃距离中假设的跳跃距离最短
#再使用二分答案(显然答案范围在0——L之间)找到最大值,不用暴力枚举,那样会超出时间限制

#使用 ‘二分答案’
#使用场景:题目中‘使得选手们在比赛过程中的最短跳跃距离尽可能长’,即求最短跳跃距离的最大值
#         当出现 最小值最大 或 最大值最小 或求最大值、最小值,考虑二分答案
#需要满足的条件:1.答案有一个确定的范围 (0——L)
#              2.答案只有一个 (求最大值显然只有一个)
#              3.答案必须满足单调性(连续上升或下降) (移走的石头越多,最短跳跃距离越大)
#写法形式:1. 缩小范围时,r=mid,l=mid+1,取中间值时,mid=(l+r)>>1
#         2. 缩小范围时,l=mid,r=mid-1,取中间值时,mid=(l+r+1)>>1

#判断假设的最短跳跃距离是否合法
def check(x):
  #上一块石头的位置
  pre=0
  count=0
  for i in range(N+1):
    #如果当前两相邻石头的距离小于最小跳跃距离,我们就动用特殊权利(移走),
    #那样上一个石头位置不变,移走的次数加1,
    #循环结束判断次数是否大于最大使用次数 M,如果大于显然这个假设漏洞太多,打了 M 个补丁依旧不中用,只好舍弃了
    #如果大于等于,那就没事了,将上一块石头挪到当前位置
    if distances[i]-pre<x:
      count+=1
    else:
      pre=distances[i]
  if count>M:
    return False
  else:
    return True

#二分答案:在答案的范围中二分,确定答案的最值
l,r=1,L  #l,r为最短跳跃距离的边界,即为答案的边界
while l<r:
  mid=(l+r+1)>>1
  #当判断 mid 合法时,说明当前选择的最短跳跃距离在承受范围内,为寻找更大的最短跳跃距离,左边界右移
  if check(mid):
    l=mid
  else:
    r=mid-1
print(l)



2.距离和

import itertools
nums=input()
#字符串同样可以使用下标
res=[]
#求元组的含有2个元素的子集
for tmp in itertools.combinations(nums, 2):
  res.append(tmp)
# print(res)
#print(res),下面为实例
# rtt
# [('r', 't'), ('r', 't'), ('t', 't')]

#当然也可以用暴力枚举的方式
ans=0
n=len(nums)
for i in range(n):
  for j in range(i+1,n):
    ans+=abs(ord(nums[i])-ord(nums[j]))
print(ans)

# 字符——>ASCII : ord()
# ASCII——>字符 : chr()
# length=len(res)
# sum=0
# for i in range(length):
#   sum+=abs(ord(res[i][0])-ord(res[i][1]))
# print(sum)

3.扩散

单源BFS和多源BFS总结

题目链接:Python真题练习2Python真题练习

差别不大,多源在队列初始化中多了几个元素

步骤:1.构造三个容器

        方向数组:存放前进的方向

        判断矩阵(或数组)或前驱字典:判断矩阵记录某位置是否被访问,在后续判断中筛选未访问过的,同时题目中的位置范围设置为判断矩阵的判断范围,没有范围的话就使判断矩阵尽量大一些即可,当然有时在一维状态下就为判断数组;前驱字典,记录被访问点及前驱字典,在后续中运用 类似 if (x,y) not in pre.keys(): 的语句判断是否被访问过,当然运用类似while pre[tmp]!=-1:  tmp=pre[tmp]  step+=1  print(step) 这些语句就可以算出走的步数

        队列(尽量使用 deque 双向队列):储存单源或多源,双向队列插入删除元素快,当为单源时可将走的步数添加进去作为元素的第二属性

           2.循环:以 队列是否为空 为结束条件,while queue: 

                 结束条件,break

                方向数组循环,判断是否否访问过,然后设置以访问、加入队列、步数加1(为单源的话就可以将元素第二属性加1)

                弹出队列第1个元素,queue.popleft()

import collections

#方向数组
dxMove=[-1,1,0,0]
dyMove=[0,0,-1,1]

#前驱字典:记录被访问点及前驱节点,通过判断是否为根可以得到走的步数
#每次访问过后记录下来
pre={(0,0):(0,0),(2020,11):(2020,11),(11,14):(11,14),(2000,2000):(2000,2000)}

#队列:记录坐标和时间(或者说是走的步数)
queue=collections.deque([(0,0,0),(2020,11,0),(11,14,0),(2000,2000,0)])
# queue.append((0,0,0))
# queue.append((2020,11,0))
# queue.append((11,14,0))
# queue.append((2000,2000,0))
ans=4
while queue:
  if queue[0][2]==2020:
    print(ans)
    break
  for i in range(4):
    x=queue[0][0]+dxMove[i]
    y=queue[0][1]+dyMove[i]
    if (x,y) not in pre.keys():
      pre[(x,y)]=(queue[0][0])
      queue.append((x,y,queue[0][2]+1))
      ans+=1
  queue.popleft()

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值