蓝桥杯备赛(1) --- 递归与递推

蓝桥杯备赛(1) — 递归与递推

概念

递归:函数存在本身调用函数本身的情况,叫做递归。递的意思是将问题拆解成子问题进行求解,子问题再进一步求解,直到无法进一步细致。归的意思就是最小子问题的求解。递归解题通用解决思路:1.一个问题可以分解成具有相同解决思路的子问题(本质就是能调用同一个函数dfs)2.经过层层分解的子问题最后一定是有一个不能再分解的固定值的(即终止条件)。当具备以上条件时,我们即可在一定T复杂度下使用递归实现。
递推:通过一定的公式或者堆砌将我们的结果从底层开始进行递推。

例题实操

Q1

递归实现指数型枚举(点击此处跳转原题)

A1

对于该问题,我们每个数字都可以是选与不选两种情况。我们不妨将0视为不选,1视为选。故我们可以利用二进制的表达来代替每个答案的结果。
比如:8 -->1000 ->第一个数选,其余数均不选。
我们可以通过位运算来获取这种结果
代码如下

n=int(input())
for i in range((1<<n)):
    lst=[]
    for k in range(n):
        if i>>k & 1:
            #说明该位为1
            lst.append(str(k+1))
    if len(lst)==0:
        print(' ')
    else:
        print(' '.join(lst))

这是我们对于题目进行理解得到的结果,但我们在考场上可能难以第一时间想到这种简洁的方式(bushi)。我们可以联想到我们的递推。通过逐个递推每个数字是否使用,直到达到最后一个数字system.out。
代码如下:

n=int(input())
st_list=[0 for i in range(n+1)]#存储每个数是否被利用
def dfs(k):
    #k:当前遍历的数字
    if k>n:
        str_lst=[]
        for i in range(1,n+1):
            if st_list[i]:
                str_lst.append(str(i))
        print(' '.join(str_lst))
        return 
    st_list[k]=1
    dfs(k+1)#用k
    st_list[k]=0#恢复现场
    dfs(k+1)#不用k
dfs(1)

两种代码均可完全AC

Q2

递归实现排列型枚举(点击此处跳转原题)

A2

典型的萝卜填坑问题,我们可以利用st_lst来存储每个位置放置的数,同时judge_lst来存储每个数据是否被利用。通过由小到大的顺序来进行dfs以满足字典序。
代码如下

n=int(input())
st_lst=[0 for i in range(n+1)]#每个位置存储的数列表
judge_lst=[0 for i in range(n+1)]
def dfs(k):
    if k>n:
        print(' '.join(list(map(str,st_lst[1:]))))
        return 
    for i in range(1,n+1):
        if judge_lst[i]==0:
            st_lst[k]=i
            judge_lst[i]=1
            dfs(k+1)#下一层遍历
            judge_lst[i]=0#恢复原状,不然全部都是exist
dfs(1)

Q3

费解的开关(点击此处跳转原题)

A3

针对该问题,我们需要从题目特征中进行分析,开关开两次相当于啥也没做,故我们对每个开关只需动一次就行,而且当我们从上往下动时,每一个只会影响其上面的,最后只需特判最后一行是否满足全亮即可(同时需要关注是否在六步之内)。于是这就是我们最典型的递推问题。
代码如下:

from copy import deepcopy
n=int(input())
dx=[0,0,1,0,-1]
dy=[0,-1,0,1,0]
def change(x,y):
    for i in range(5):
        a=x+dx[i]
        b=y+dy[i]
        if a>=0 and a<5 and b>=0 and b<5:
            mat_copy[a][b]^=1#取反操作
for _ in range(n):
    cnt=10
    drag_mat=[list(map(int,list(input()))) for _ in range(5)]
    #这里由于第一行的特殊情况,我们需要进行枚举,不然的话就不一定是最优解
    #因为我们是从第二行开始操作的,故需要枚举第一行的操作。
    for num in range(32):
        #进行深复制,防止对原来的Mat造成影响
        mat_copy=deepcopy(drag_mat)
        ans=0
        for k in range(5):
            if (num>>k)&1:
                change(0,k)
                ans+=1
        for i in range(4):
            for j in range(5):
                if mat_copy[i][j]==0:
                    change(i+1,j)
                    ans+=1
        #对最后一行进行特判
        for j in range(5):
            if mat_copy[4][j]==0:
                ans=10
        cnt=min(cnt,ans)
    if cnt>6:
        cnt=-1
    print(cnt)
    try:
        _=input()
    except:
        break

Q4

递归实现组合型枚举(点击此处跳转原题)

A4

该题可借鉴排列型枚举的思路,萝卜填坑。
代码如下:

n,m=map(int,input().split())
num=[]#引入动态数组存储结果
def dfs(k):
    if len(num)>m or len(num)+(n-k+1)<m:
        #长度超过m或者剩余的数无法到达m,则采取剪枝,break即可
        return 
    if k==n+1:
        print(' '.join(list(map(str,num))))
    num.append(k)#选择k
    dfs(k+1)
    #回溯
    num.remove(k)
    dfs(k+1)#不选择k
dfs(1)
#体会一下动态数组的强大!

Q5

带分数(点击此处跳转原题)

A5

解法1:该题典型的全排列问题,我们可以首先进行全排列,然后进行a|b|c的形式进行分割,然后进行判决。
代码如下:

n=int(input())
count=0
exit_list=[0 for i in range(9)]
num_list=[0 for i in range(9)]
def calc(l,r):
    res=0
    for i in range(l,r+1):
        res=res*10+num_list[i]
    return res
def dfs(num):
    global count
    if num==9:
        for i in range(7):
            a=calc(0,i)
            if a>n:
                continue
            for j in range(i+1,8):
                b=calc(i+1,j)
                c=calc(j+1,8)
                if (a*c+b==c*n):
                    #转换成乘法(算法中常用的思想)
                    count+=1
        return 
    for i in range(9):
        if not exit_list[i]:
            exit_list[i]=1
            num_list[num]=i+1
            dfs(num+1)
            exit_list[i]=0#回到初始状态
dfs(0)
print(count)

解法二:我们可以先对a进行排列型枚举,再对b进行,然后通过判决等式进行剪枝(c是否满足我们的条件:全选且不重复)。思路简单,但实操起来代码是比较难理解的。
代码如下:

N=9
n=int(input())
had_use=[0 for i in range(N)]
ans=0
def check(a,c):
    b=n*c-a*c
    ever=had_use.copy()
    while b:
        #对b的每一位进行判断
        t=b%10#取它最后一位,来获取每一位的值(取余操作)
        b//=10#(将最后一位去掉)
        if (t==0)|(ever[t-1]):
            return False
        ever[t-1]=1
    for i in range(N):
        if not ever[i]:
            return False
    return True
def dfs_c(x,a,c):
    global ans,had_use
    if x>N:#在这里判断是否超限制,妙!
        return
    if check(a,c):
        ans+=1
    for i in range(N):
        if not had_use[i]:
            had_use[i]=1
            dfs_c(x+1,a,c*10+i+1)
            had_use[i]=0
def dfs_a(x,a):
    global had_use
    if a>=n:
        return 
    if a:
        dfs_c(x,a,0)
    for i in range(N):
        if not had_use[i]:
            had_use[i]=1
            dfs_a(x+1,a*10+i+1)
            had_use[i]=0
    
dfs_a(0,0)
print(ans)

Q6

飞行员兄弟(点击此处跳转原题)

A6

这题思路与费解的开关一致,并未变得更加复杂。但调试代码比较麻烦,不容易一次性写对。
代码如下:

bridge_matrix=[]
mat=[]
count=10000
num=0
for i in range(4):
    mat.append(list(input()))
def change(i,j):
    global bridge_matrix
    for t in range(4):
        bridge_matrix[i][t]='-'if bridge_matrix[i][t]=='+' else '+'
        bridge_matrix[t][j]='-'if bridge_matrix[t][j]=='+' else '+'
    bridge_matrix[i][j]='-'if bridge_matrix[i][j]=='+' else '+'
def check():
    for i in range(4):
        for j in range(4):
            if bridge_matrix[i][j]=='+':
                return False
    return True
def dfs():
    global bridge_matrix,count,num
    for i in range(1,2**16):
        #print(i)
        bridge_matrix=mat.copy()
        t=0
        for j in range(15,-1,-1):
            state=(i>>j)&1
            ind=(15-j)//4
            col=(15-j)%4
            if state:
                change(ind,col)
                t+=1
        if check()and(t<count):
            num=i+1
            count=t+1
    print(count)
    for j in range(15,-1,-1):
        state=(num>>j)&1
        ind=(15-j)//4
        col=(15-j)%4
        if state:
            print(str(ind+1)+' '+str(col+1))
dfs()

Q7

翻硬币(点击此处跳转原题)

A7

该题与费解的开关也很类似,而且更为简单。我们也可以利用递推来求解。做这种题就应该想到我们的操作顺序以及次数是否会对原有state有何影响。若满足操作顺序、偶数次数没影响时,阔以利用递推的思维来进行判断
代码如下:

str_init=list(input())
str_target=list(input())
count=0
def turn(i):
    global str_init
    for t in range(i,i+2):
        str_init[t]='*' if str_init[t]=='o' else 'o'
def check(i):
    if str_init[i]==str_target[i]:
        return 0
    return 1
def dfs():
    global count
    for i in range(len(str_target)-1):
        if check(i):
            turn(i)
            count+=1
dfs()
print(count)

总结

我们可以得知,递推和递归的思想可以用来求解操作方式单一、问题可以逐步化解、操作顺序对结果无影响的问题。当然个人总结比较片面,仅供参考。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值