上升子串【第十一届第三场】【省赛】【B组】Python 【记忆化搜索dfs+朴素dfs(学习写法)】

本文介绍了如何使用记忆化搜索优化算法来计算矩阵中所有上升子串的数量。通过dfs递归和状态转移方程,避免了重复计算,特别强调了从长度为2子串的重叠问题出发的改进。通过携带目标长度参数,简化了递归出口条件,提高了效率。
摘要由CSDN通过智能技术生成

题目描述
在这里插入图片描述
找到所有上升子串的数量,很明显dfs,在找长度为3肯定会用到长度为2的子串存在重叠子问题,优化用记忆化搜索,

for循环每一点的上升子串的数目都搜出来记录下来,最终求和f

f[x][y]代表x y 点出发的上升子串数目

注意这里的一个区别,就是进入下一层dfs之前f[x][y]=1,因为一个字符也算

M,N=510,510
f=[[-1]*10 for i in range(10)]#代表x y 点出发的上升子串数目
dire=[[1,0],[-1,0],[0,1],[0,-1]]
mat=[["A","B","C"],["B","C","A"]]

n,m=2,3
def dfs(x,y):#以x,y为起点搜索以该点为起点的所有上升序列的长度记录在fx,y中
  global n,m
  if f[x][y]!=-1:return f[x][y]
  
  f[x][y]=1#本身算一个 ,注意这里和其他记忆化搜索的区别!!!
  
  for d in dire:
    nx,ny=x+d[0],y+d[1]
    #print((x,y),"=>",(nx,ny))
    if 0<=nx<n and 0<=ny<m and mat[nx][ny]>mat[x][y] :#d第一次写 范围写错了,注意!
      f[x][y]+=dfs(nx,ny)
  return f[x][y]


ans=0
for i in range(0,n):
  for j in range(0,m):
    ans += dfs(i,j)
print(ans)

  
cnt=0
for i in range(0,n):
  for j in range(0,m):
    if f[i][j]==-1:continue
    cnt +=f[i][j]
print(cnt)


朴素的dfs写法
肯定要先写退出条件,一开始想到搜到 最左下角就退出,但是这样会少搜了以左下角为起点向四周的结果;而且因为上升子串的长度不唯一,每次结束的条件都不一样,不像是n皇后,搜到n行就可以退出,如果要真的写,为dfs携带参数,记录数目。。。。好像也不太好写,最好的还是dfs一点就记录在备忘录里

我们的target是记录所有点的上升序列,而上升序列最长也就26!
我们让dfs携带参数target 目标长度,和当前长度count ,如果target==count 就说明找到,记录一次!这便是递归的出口!!!!!

我们遍历所有上升序列长度的可能性,1-26,再遍历每一个点的上升序列长度,累加!

参考别人思路写的一个朴素的dfs,效率挺低

dire=[[1,0],[-1,0],[0,1],[0,-1]]
mat=[["A","B","C"],["B","C","A"]]

n,m=2,3
summ=0
def dfs(x,y,count,target):
  global summ,n,m
  if count == target:
    #print(count)
    summ+=1
  else:
    for d in dire:
      nx,ny=x+d[0],y+d[1]
      #print((x,y),"=>",(nx,ny))
      if 0<=nx<n and 0<=ny<m and mat[nx][ny]>mat[x][y]:
        #print((nx,ny),"==")
        count +=1
        dfs(nx,ny,count,target)
        count -=1



for target in range(1,27):
  for i in range(0,n):
    for j in range(0,m):
      dfs(i,j,1,target)
print(summ)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值