python算法从入门到实践(章一):枚举(暴力)算法

前言:

算法,简单理解就是“解题思路”,要找到解题的方法和关键的运算条件语句

本文基于《Python算法从入门到实践》薛小龙老师编著的这本书,佐以笔者的思考而成,主要内容为时空复杂度的简单介绍,枚举算法的详细阐述与应用

一、算法需要的一点基础:时间复杂度和空间复杂度

1.时间复杂度

O(n),n代表复杂度是n倍的级别(还有别的),复杂度越大,代码最坏情况下(就是数据量特别大的时候)跑的时间就越久,效率越低

下面是一篇大佬的总结,我就抛砖引玉了

详解时间复杂度计算公式(附例题细致讲解过程)_时间复杂度怎么算-CSDN博客

2.空间复杂度

代码跑起来所占临时空间的大小经常牺牲空间复杂度换取时间复杂度,但是也不一定,具体情况具体分析

赋值语句空间复杂度为1,列表的空间复杂度是列表长度,但还是常数级,属于O(1)范畴

下面是一篇大佬的总结,我就抛砖引玉了

时间复杂度与空间复杂度详解 —— 如何计算 + 如何表示_如何理解空间复杂度的表示-CSDN博客

二、枚举法

1.常用语句

暴力破解,使用for循环或者while循环,逐个列举,逐个排查,比较费事,但准确率高

2.解题思路

①确定枚举对象,枚举范围和判断条件

②逐一列举可能的解,验证是否为本问题的答案

3.解题步骤

①猜可能解的范围,做到不重不漏

②找到判断真解的条件(把题目的条件进行翻译)

③缩小可能解的范围,找到另一个隐藏条件,缩圈再运算,提高效率

三、解题实例

1.开胃小菜:求一个符合条件的五位数

算法描述题x算=题题题题题

不同的字代表不同的数字,请找到“算法描述题”对应的五位数

按步骤解题: 

①确定枚举对象,猜范围:求的是某个五位数,那么最明显的范围就是(10000,99999)

②找判断条件:各个位的数均不同(这个不好描述,pass),个位数与11111的某个倍数的每一位数值相同(这个关于到位置,可以用字符串,选取)

③缩圈:还注意到,这个不重复的五位数可以整除11111(可以用整除缩小范围)

完整代码如下:

for i in range(10000,99999):#遍历五位数
    for j in range(0,10):#“算”不能是0.1.2
        if i*j%11111==0: #11111的倍数
            if len(str(i))==len(set(str(i))): #五个位都不同
                if str(j)==str(i)[0]: #“题”相同
                    print(i)

2.关灯问题

1表示亮,0表示灭,下面的图引用知乎

Flip Game(又名翻转游戏、点灯游戏、灭灯游戏)的游戏技巧是什么? - 家飞猫的回答 - 知乎
https://www.zhihu.com/question/22716573/answer/62708510

import numpy as np
line=[[0]*6]* 5
for i in range(5):
    line[i] = input("请输入第"+ str(i)+"行:").split(',')#将 line 中的元素转换为整型
    line[i] = list(map(int, line[i]))

puzzle = np.array(line)
zero =np.zeros(6)
#向puzzle 中的最上面加入一行 0
puzzle =np.insert(puzzle,0,values=zero,axis=0)
#向puzzle 中的最后一列加入一列 0
puzzle =np.insert(puzzle,6,values=zero,axis=1)
#向puzzle 中的第0列加入一行 0
puzzle =np.insert(puzzle,0,values=zero,axis=1)
b=[[0 for col in range(8)]for row in range(6)]
#6*8 不要写反
press = np.array(b)
#或press=np.zeros((6,8))

def guess():
    for r in range(1,5):
        for c in range(1,7):
#根据 press 的第一行和 puzzle 的第一行,确定 press 其他行的值
            press[r + 1][c]=(puzzle[r][c]+press[r][c]+ press[r - 1][c]+press[r][c - 1]+press[r][c + 1])%2#判断所计算的 press 能否熄灭最后一行的所有灯
    for c in range(1,7):
        if(press[5][c - 1]+ press[5][c] + press[5][c + 1]+ press[4][c])%2 !=puzzle[5][c]:
            return 0
    return 1
    #枚举第一行按下开关的所有可能性,有2~6个
def enumeration():
    while guess()==0:
        press[1][1]+=1
        c =1
        while(press[1][c]>1):
            press[1][c]=0
            c += 1
            press[1][c]+= 1
        continue

enumeration()
print("灯的初始状态:\n",puzzle[1:6,1:7])
print("按下结果为:\n",press[1:6,1:7])

上面的代码实现的是按一次的结果

3.青蛙踩水稻问题

稻田里有青蛙,青蛙会踩着水稻跳出去,同一个水稻田有多只青蛙的跳跃路径,农夫只能看见x-y方向的青蛙路径,询问:跳的最多的青蛙跳了几步?

这是完整的实现代码

max=2
RC = input("请输入水稻的行数和列数:").split(' ')
RC = list(map(int,RC))
n = int(input("请输入被踩踏的水稻数目:"))# 输入被踩踏的水稻坐标
plants=[[0,0]]*n
plant =[0,0]
for i in range(n):
    plants[i]= input("请输入第"+ str(i + 1)+"棵被踩踏的水稻的坐标:").split(' ')
    plants[i]= list(map(int, plants[i]))
def searchpath(secplant,dx,dy):
    plant[0]= secplant[0] + dx
    plant[1]= secplant[1] + dy
    steps =2
    RC =[6,7]
    while 1 <= plant[0] <= RC[0] and 1 <= plant[1] <= RC[1]:
        if plant not in plants:
            steps = 0
            break
        plant[0] += dx
        plant[1] += dy
        steps += 1
    return steps

# 将被踩踏的水稻按坐标大小进行排序,先比较,若x相等,则比较y
plants = sorted(plants)
for i in range(n -2):
    for j in range(i + 1, n - 1):
    # 选取 plants[i]为第一个点,plants[j]为第二个点,求取间距 dX 和 dy
        dX = plants[j][0]- plants[i][0]
        dY = plants[j][1] - plants[i][1]
    # 判断选取的第一个点的前一个点的坐标是否在稻田里
    # 若在,说明选取的步长太小,换第二个点再试
        pX = plants[i][0] - dX
        pY = plants[i][1] - dY
        if 1 <= pX <= RC[0] and 1 <= pY <= RC[1]:
            continue
    # 跳跃 max步后,判断其在x方向是否过早越界
    # 其中max是程序实时比较计算出的最大步数
    # 由于 plants 是排序过的,当这个点越界时,换第二个点,dx增大
    # 那么无论怎么换第二个点,都一定越界,因此,换第一个点再试
        if plants[i][0]+(max-1)* dX> RC[0]:
            break
    # 跳跃 max步后,判断其在¥方向是否过早越界
    # 由于 plants 是排序过的,换第二个点时,dx增大,但是 dy 减小
    # 因此,如果越界,则需换第二个点再试
        pY = plants[i][1] + (max - 1) * dY
        if pY > RC[1] or pY < 1:
            continue
    # 走到这步说明,该条路径不仅符合条件,而且跳跃步数比之前的max要大#因此,计算该条路径的步数
        steps = searchpath(plants[j], dX, dY)
        if steps > max:
            max = steps
    if steps == 2:
        max = 0
print(max)

以下为测试结果

以上为笔者的学习笔记,如有不妥,欢迎各位批评指正0v0

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值