【算法题】机试指南-递归、分治、搜索篇

本文仅供个人学习使用,免费分享。每日更新,建议关注收藏!
目前[机试指南]本系列已经达到字数10w+,所以按原每个章节拆开发布,点击目录跳转。

本站友情链接:

  1. c/c++算法题指南
    严书代码
    c/c++大复习1
    c/c++大复习2
  2. python算法题指南
    牛客华为机试103精华
    python输入输出大全
    python语法
    PAT甲级真题刷题笔记 共179道
  3. python官方文档
    python官方文档
  4. 机试指南系列
    基础篇
    贪心篇
    递归分治搜索篇
    数据结构进阶篇(树/图/优先队列)
    数学问题篇
    动态规划篇
    STL篇

递归与分治

递归需要具备的条件:子问题与原问题相同只是规模更小+有出口

例题8.2 汉诺塔
www.lintcode.com/problem/169/


class Solution:
    """
    @param n: the number of disks
    @return: the order of moves
    """
    result=[]
    def move(self,_from,_to):
        self.result.append("from {} to {}".format(_from,_to))
        #print(self.result)

    def hanoi(self,n,_from,_to):
        for item in ['A','B','C']:
                if(item !=_from and item!=_to):
                    other=item
        if(n==1):
            self.move(_from,_to)
            return
        else:
            self.hanoi(n-1,_from,other)
            self.move(_from,_to)
            self.hanoi(n-1,other,_to)
            return

    def tower_of_hanoi(self, n: int) -> List[str]:
        # write your code here
        self.result=[]
        self.hanoi(int(n),'A','C')
        return self.result

习题8.1 杨辉三角形(西北工业大学复试上机题) http://t.cn/Ai0KcLRI 链接失效
习题8.2 全排列(北京大学复试上机题) http://t.cn/Ai0K0hXZ

#最简单的方法
import sys
import itertools
for line in sys.stdin:
    a = list(line)
    a=a[:-1]
    for item in itertools.permutations(a,len(a)):
        print(''.join(item))
        
#老老实实方法
import sys
result=[]

def permutation(l,num,s):
    global result
    #print('l,num,s',l,num,s)
    for i in range(len(l)):
        if(num==0):
            if(s not in result):
                result.append(s)
               # print(result)
            return 
        if(l[i] not in s):
            s+=l[i]
            permutation(l,num-1,s)
            s=s[:-1]
        else: continue
    return result


for line in sys.stdin:
    a = list(line)
    a=a[:-1]
    #print(a)
    s=''
    result=permutation(a,len(a),s)
    for item in result:
        print(''.join(item))
    

分治:分而治之,把原问题分解成多个小的彼此互相独立且与原问题相同或相似的子问题,直到最后的子问题直接能够求解。所以会使用递归。步骤3步,分、治、合。
由此可区分开,快排是递归,归并排序是分治。

习题8.3 2的幂次方(上海交通大学复试上机题) http://suo.im/5D5hnX
做这种题的秘诀就是先举个小的例子写递归,然后进行嵌套即可

import sys
dp=['0','2(0)','2','2+2(0)','2(2)'] #1,2,3,4
num=['']*14


def divide(x,s,flag):
    #if(flag==1):print('into subprogram')
    bi_a=bin(x)
    bi_a=str(bi_a)[2:]
    j=len(bi_a)
    #print('bi_x',bi_a)
    for i in range(len(bi_a)):
        j-=1
        if(bi_a[i]=='1'):
            #print(j)
            if(j>2):
                s+='2('
                flag=1
                s=divide(j,s,flag)
                s+=')'
                flag=0
                #print('out subprogram',s)
            elif (j==0):
                s+='2(0)'
            elif (j==1):
                s+='2'
            elif(j==2):
                s+='2(2)'
            s+='+'
    
    s=s.rstrip('+')
    if(flag==0):
        dp.append(s)
    #print('every end ',s)
    return s

for line in sys.stdin:
    a = int(line)
    flag=0 # 1时还在子程序里
    s=''
    '''
    for i in range(5,15):
        print('jiexi',i)
        divide(i,s,flag)
    print(dp[8:])
    '''
    divide(a,s,flag)
    print(dp[-1]) 
    #print(dp)

搜索

深搜

先一直深度搜索该点的邻居节点,直到找到解,或者无法再访问下一个节点,则回溯,访问上一个节点的另一个邻居。

  1. 不具备最优特性,不像bfs。所以 用来判断题目是否有解
  2. 实现方式2种:利用堆栈,先进后出,保存和扩展搜索过程中得到的状态; 或者使用递归实现;

例题9.3 A Knights’s Journey http://poj.org/problem?id=2488

例题9.4 Square http://poj.org/problem?id=2362

若已经拼成3条木棍,则可以构成正方形,因为总长/4=木棍长度,且已经拼出3条,剩下一条必也是木棍长度

剪枝思路:

  1. 总长不能整除4,则必无法构成正方形
  2. 某根木棍长度大于总长/4,则必无法构成正方形
  3. 发现当前长度木棍仍然拼上大于总长/4,之后的长木棍可跳过(需排序)
  4. 棍长==0的跳过
  5. 记录拼凑的失败棍子长度,等于时直接跳过

注意:全部可能都尝试完后 记得book数组该下标的状态恢复,取消标记

习题9.1 神奇的口袋(北京大学复试上机题) http://t.cn/Ai0u0GUz
需要考虑几个注意点:

  1. ==40还需要往后继续遍历,因为有可能后一个和这个值相同;如果>40,则没必要往后遍历了,因为items是从小到大排列的
  2. result.append(book)会导致result里面的值一直跟着book的值变化,导致book 始终in result,所以需要深拷贝
  3. 优化复杂度防止超时:由于可能出现(i,j)(j,i)两种重复计算但实质上是同一种情况,设置index去重,也就是说循环只可能选择大于index的下标进行探查
import sys
import copy
book=[0]*20
result=[]
def dfs(cnt,items,book,now,index):
    #print('now',now)
    for i in range(len(items)):
        if(not book[i] and i>index):
            now+=items[i]
            book[i]=1
            #print('now+items{}:{}'.format(i,items[i]),cnt,book,now)
            if(now==40 ):
                #print(book not in result,book,result)
                if(book not in result):
                    cnt+=1
                    result.append(copy.deepcopy(book))
                    #print('r',result)
                #print('cnt++',cnt)
                now-=items[i]    
                book[i]=0
                #print('cancel items',i,book)
                
            elif(now>40):
                now-=items[i]    
                book[i]=0
                #print('cancel items',i,book)
                break
            else:
                cnt=dfs(cnt,items,book,now,i)
                now-=items[i]   
                book[i]=0
                #print('cancel items',i,book)
    
    return cnt
                    

for line in sys.stdin:
    a = int(line)
    items=[]
    for i in range(a):
        items.append(int(input()))
    items=sorted(items)
    #print(items)
    cnt=0
    cnt=dfs(cnt,items,book,0,-1)
    print(cnt)

#解法2
import sys
def dfs(v,i,temp,cnt,book):
    #print(i,temp,cnt,book)
    if(temp==40):
        cnt+=1
        return cnt,book
    elif(temp>40):
        return cnt,book
    else:
        for j in range(i,len(v)):
            if(book[j]==0):
                book[j]=1
                temp+=v[j]
                cnt,book=dfs(v,j+1,temp,cnt,book)
                book[j]=0
                temp-=v[j]
    #print(i,temp,cnt,book)
    return cnt,book

for line in sys.stdin:
    n = int(line)
    v=[]
    book=[0 for i in range(n)]
    #print(book)
    temp=0
    cnt=0
    for i in range(n):
        v.append(int(input()))
    cnt,book=dfs(v,0,temp,cnt,book)
    print(cnt)
    

习题9.2 八皇后(北京大学复试上机题) http://t.cn/Ai0uOazs

1.核心是这个if xi == x or yi ==y or abs(xi-x)==abs(yi-y)判断
2.append()这里也要深拷贝
3. 报错信息解决

TypeError: sequence item 0: expected str instance, int found
因为list包含数字,不能直接转化成字符串。 
解决方法: "".join(str(x) for x in _list) 将数字先转化成字符格式

import sys
import copy
def check(temp,xi,yi):
    for i in range(len(temp)):
        oxi=i
        oyi=temp[i]
        if xi==oxi or abs(xi-oxi)==abs(yi-oyi): #这里oyi==yi可以省略了 
            return 0
    return 1

def queen(result,temp):
    '''
    i是行 temp[i]是列 result保存结果
    '''

    if(len(temp)==8):
        if(temp not in result):
            result.append(copy.deepcopy(temp))
        #print('ok',temp,result)
        return temp,result
    
    for i in range(8): #0~7 column
        if(i+1 not in temp and check(temp,len(temp),i+1)):
            temp.append(i+1)
            #print(i,'into sub_p',temp,result)
            temp,result=queen(result,temp)
            #print(i,'out sub_p',temp,result)
            temp.pop()
    return temp,result


for line in sys.stdin:
    n = int(line)
    result=[]
    
    temp,result=queen(result,[])#to start with row0
    result=sorted(result)
    _list=result[n-1]
    #print(_list)
    print("".join(str(x) for x in _list))

https://www.lintcode.com/problem/778
高要求版dfs/bfs,功能很容易实现,但很容易超时
卡了很久,重点复习!!

def inbound(x, y, n, m):
    return 0 <= x < n and 0 <= y < m
    
class Solution:
    """
    @param matrix: the given matrix
    @return: The list of grid coordinates
    """
    def pacificAtlantic(self, matrix):
        # write your code here
        if not matrix or not matrix[0]:
            return []
        n, m = len(matrix), len(matrix[0])
        p_visited = [[False] * m for _ in range(n)]
        a_visited = [[False] * m for _ in range(n)]
        #极大减少复杂度,反向思维
        for i in range(n):
            self.dfs(matrix, i, 0, p_visited)
            self.dfs(matrix, i, m - 1, a_visited)
        for j in range(m):
            self.dfs(matrix, 0, j, p_visited)
            self.dfs(matrix, n - 1, j, a_visited)
        res = []
        for i in range(n):
            for j in range(m):
                if p_visited[i][j] and a_visited[i][j]:
                    res.append([i, j])
        return res
    
    def dfs(self, matrix, x, y, visited):
        visited[x][y] = True
        dx = [0, 1, 0, -1]
        dy = [1, 0, -1, 0]
        for i in range(4):
            n_x = dx[i] + x
            n_y = dy[i] + y
            if not inbound(n_x, n_y, len(matrix), len(matrix[0])) or visited[n_x][n_y] or matrix[n_x][n_y] < matrix[x][y]:
                continue
            self.dfs(matrix, n_x, n_y, visited)

广搜

解题思想:

  1. 状态,确定基本状态,
  2. 扩展,有几种搜索(扩展)状态的方法
  3. 有效,不在范围内的状态就舍去,或者标记
  4. 队列,将同一轮几种搜索方法得到的有效状态都放入队尾,每次取队头进行扩展
  5. 最优,bfs具备最优特性,看到题目有最少、最短、最优,考虑是否用bfs

例题9.1 Catch That Cow http://poj.org/problem?id=3278
例题9.2 Find The Multiple http://poj.org/problem?id=1426
这个题目如果按最简单的思路,挨个枚举出n的倍数进行测试,该题目n的倍数m不超过100位(要使用longlong lld),计算量非常大。应当反过来想,搜索仅有0,1组成的数,看是否能整除n即可。有两种搜索状态,10num ,10num+1,将num初始化=1

习题9.1 玛雅人的密码 http://t.cn/Ai0lUhJj
将原始字符串看作树的根节点,进行一次交换的字符串作为子节点,依次往下交换,然后使用广度优先搜索(BFS)遍历这棵树,也就是层序遍历,每层的字符串对应一个 Map 值,含有2012的字符串在哪一层,就输出该层的 Map 值,如图所示。保证树上的每个结点都不相同,直到穷尽。
认真读题:每次只能移动相邻的两个数字。

import sys
def swap(s,i,j):
    #print(i,j)
    if(j+1<len(s)):
        s=s[:i]+s[j]+s[i]+s[j+1:]
    elif(j+1==len(s)):s=s[:i]+s[j]
    else:s=s[:i]
    return s
def bfs(s,book): #1.需要队列
    queue=[]
    queue.append([s,0])#queue初始化
    book.append(s)#book数组
    while(len(queue)): #队列非空时开始处理
        now=queue[0]
        queue=queue[1:]
        now_s=now[0]
        if(now_s.find('2012')!=-1):#跳出条件
            print(now[1])
            return 
        for i in range(len(now_s)):#执行放入queue
            now_s=swap(now_s,i,i+1)
            if(now_s not in book):#放入queue的条件
                queue.append([now_s,now[1]+1])
                book.append(now_s)
            now_s=swap(now_s,i,i+1)#撤回操作
    print(-1)#都不满足条件的最后跳出
for line in sys.stdin:
    a = int(line)
    string=input()
    bfs(string,[])
    #print('2111'.find('2012')==-1)#不存在时返回的是-1 type==int

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值