算法疑难杂题

1.盛最多水的容器,提供一个数组,为板子的高度,索引差为宽度,选出两块板子,是的组成的容器容积最大。暴力选择为C(n,2),当然搜索时,为了简化,注意短板效应,容积大小取决于短板和宽度。使用双指针指向数组两端,每次比较两个指针指向板子的高度,固定高的板子,向内移动短的板子,因为向内移动宽度减少,同时,由于短板效应,只能说固定长版,移动短板,期望短板变长以此来获得更大值。

class Solution:
    def maxArea(self, height: List[int]) -> int:
        i,j,res = 0,len(height)-1,0
        while i

2.N叉树的前序遍历,使用递归或者迭代,注意两者之间的区别。

# N叉树通用递归模板
class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        res = []
        def helper(root):
            if not root: # 递归中止条件
                return
            res.append(root.val) # 操作处理
            for child in root.children:
                helper(child) # 调用递归函数
        helper(root)
        return res

# N叉树迭代方法
class Solution:
    def preorder(self, root: 'Node') -> List[int]:
        if not root:
            return []
        s = [root]
        # s.append(root)
        res = [] # 记录子节点的堆栈
        while s:
            node = s.pop()
            res.append(node.val)
            # for child in node.children[::-1]:
            #     s.append(child)
            s.extend(node.children[::-1]) # extend()直接加上一个list。和append()不同。
        return res

3.有三种葡萄,每种分别有a,b,c颗。有三个人,第一个人只吃第1,2种葡萄,第二个人只吃第2,3种葡萄,第三个人只吃第1,3种葡萄。适当安排三个人使得必须吃完所有的葡萄,并且且三个人中吃的最多的那个人吃得尽量少(主要)。

将三个葡萄数想像成三条线段,三个人想象为三个顶点,则如果能构成三角形(符合两短相加大于长),则三个人一人吃掉相邻两条边的一部分就可以;如果不能构成三角形(即有一超长边),那么要把超长边平分给两个人吃,相当于折断长边,现在有4条边构成四边形,则只要两个人吃完长边后继续吃相应短边那么也没有问题,但要是两个人吃完长边后不再吃相应短边有两种情况:

  1. 两个人吃完长边后不再吃短边,第三人吃完短边也没有超出另两个人;
  2. 两人吃完长边后,如果不帮第三人吃两个短边,会使第三人吃的超过2人。

在尽量少的情况下,第一种情况的吃的最多的就是长边的1/2;第二种情况则与三角形情况相同,需要所有人均分。
因此,综合来看只有两种情况:所有人平分,或者其中两人平分最多的那种葡萄。这两个哪个大,输出哪个。

t=int(input())
for _ in range(t):
    a,b,c=map(int,input().split())
    maxnum=max(a,b,c)
    total=a+b+c
    if maxnum//2>=total-maxnum: # 两短边之和小于长边的一半
        if maxnum%2==0: 
            print(maxnum//2)
        else:
            print((maxnum+1)//2) # 为了向上取整
    else: # 两短边之和大于长边的一半
        if total%3==0:
            print(total//3)
        elif total%3==1:
            print((total+2)//3)
        else:
            print((total+1)//3)

4.将一个数拆分成多个素数之和,求出所有可能的情况。

  1. 给定的数字下有哪些质数,通过遍历找到比该数下的质数列。
  2. 在遍历时,需要判断一个数是否为质数。
  3. 得到质数列后,需要DFS搜索出所有可能的情况。
def judge_prime(n):
    if (n == 0 or n == 1): return False
    if (n == 2): return True
    if (n % 2 == 0): return False
    # 判断
    if 0 in [n % i for i in range(2, int(sqrt(n) + 1))]: # 判断一个数是否为质数
        return False
    return True

def equal_prime(n):
    '''n拆分成素数之和'''
    plist = [i for i in range(n + 1) if judge_prime(i)] # 遍历得到不大于它的质数列
    DFS(n, 0, 0, plist, S=set()) # 进行DFS搜索
    
def DFS(n, index=0, sum_num=0, primes=[], L=[], S=set()): # L存储一次可能情况,S存所有情况
    if (sum_num > n): # 搜索结束的条件1
        return
    if (sum_num == n): # 搜索结束时的条件2,找到了这样的一组数字
        if (tuple(L) not in S):  # 避免重复输出
            print(L)
            S.add(tuple(L)) # S是一个集合,L也要转化为元组,是为了避免append()的浅拷贝
            
    # 只要index没有超过素数表primes的长度,就可以继续选择,超过了则不操作,迭代返回上一层
    if (index < len(primes)):
        L.append(primes[index]) # 存入后向下搜索
        DFS(n, index + 1, sum_num + primes[index], primes, L, S) 
        L.pop() # 进行回溯
        DFS(n, index + 1, sum_num, primes, L, S) # sum跳过了当前值
equal_prime(30)

2020/08/08 网易算法内推笔试题

5.牛牛现在有n个正整数的数组a,牛牛可以将其中的每个数a[i]都拆成若干个和为a[i]的正整数,牛牛想知道拆后(也可以一个都不拆)这个数组最多能有多少个素数。示例1 输入 3 1 1 1 输出 0 说明:由于1不能再拆,并且1不是素数,所以拆后最多有0个素数

# 想要尽可能多的素数,则尽可能分解为2,除不尽的可以变成3。
n = int(input())
a = [int(item) for item in input().strip().split()]
count = 0
for i in range(n):
    count += a[i] // 2 # 变相求该数最多由多少个2组成。
print(count)

6.现在有n个人排队买票,已知是早上8点开始卖票,这几个人买票有两种方式:
第一种是每一个人都可以单独去买自己的票,第1个人花费a秒。
第二种是每一个人都可以选择和自己后面的人起买票,第i个人和第i+1个人共花费印秒。
最后一个人只能和前面的人起买票或单独买票。
由于卖票的地方想早些关门,所以他想知道他最早几点可以关门,请输出一个时间格式形如:08:00.40 am/pm。时间的数字要保持2位,若是上午结束,是am,下午结束是pm
输入描述:
第一行输入一个整数T,接下来对于每组测试数据:输入一个数n,代表有n个人买票。
接下来n个数,代表每一个人单独买票的时间a[i].
接下来n个数,代表每一个人和他前面那个人起买票需要的时间b[i]

输出:对于每组数据,输出一个时间表示关门的时间

# dp[i]是前i个人完成排队的最小时间。dp[0]=0,为前0个人,dp[1]为前1个人,dp[n]为前n个人。
# 由于dp的设定,虽然题目说是第i个人要么自己买,要么和i+1买,但若两人一起买,则是dp[i+1]的值,因
# 此可以看作,每个人有两种选择,第一种自己买,第二种和前面一个人一起买,因为是前i个人,所以不考虑# 和后面那个人一起买的情况
T = int(input())
for t in range(T):
    n = int(input())
    if n == 0:
        print('08:00:00 am') # 注意特殊值
        continue
    elif n == 1:
        ans = int(input())
    else:
        a = [int(item) for item in input().strip().split()]
        b = [int(item) for item in input().strip().split()]
        dp = [0 for _ in range(n + 1)] # 申请dp数组
        dp[1] = a[0] # 前1个人,则dp[1]=a[0],但若人数大于1,则不一定第一个人就自己买。
        for i in range(2, n + 1):
            dp[i] = min(dp[i - 1] + a[i - 1], dp[i - 2] + b[i - 2])  # 要么单独自己买,要么和前面的人一起买
        ans = dp[n]
    hour = ans // 3600 + 8
    mins = ans % 3600 // 60
    miao = ans % 3600 % 60
    if hour <= 12:
        print(str(hour).rjust(2,'0') + ':' + str(mins).rjust(2,'0') + ':' + str(miao).rjust(2,'0') + ' ' + 'am')
    else:
        print(str(hour-12).rjust(2,'0') + ':' + str(mins).rjust(2,'0') + ':' + str(miao).rjust(2,'0') + ' ' + 'pm')

7.给出n个物品和它对应的价值,可以舍弃几件物品,现在要将物品分给两个人,两个人得到的物品数量可以不一样,但是得到的价值必须是一样的,问最少可以舍弃多少价值的物品?

输入为T(有多少组案例),n(物品数量),a(有n个数字,a[i]表示第i个物品的价值)。

输出为一个数字,表示最少可以丢弃的物品价值。

思路:对于每一件物品,都会存在三种选择:1、给第一个人。2、给第二个人。3、舍弃这件物品。所以可以将问题转化为树,进行深度优先搜索,只需要搜索时寻找到达最后一件物品时,两个人手里物品的价值相同,并且找到舍弃价值最小的那一个节点即可。

import sys
T = int(sys.stdin.readline().strip())
res = float('inf')

def dfs(op, x, y):
    global res # 重新声明为全局变量,调用函数外的变量
    global length
    global sum1
    global nums
    if op >= length: # op为选择货物的index
        if x == y:
            res = min(res, sum1-x-y) # 目前的最小值和sum1-x-y得到的比较
        return
    
    dfs(op+1, x+nums[op], y)
    dfs(op+1, x, y+nums[op])
    dfs(op+1, x, y) # 进行回溯

for idx in range(T):
    n = int(sys.stdin.readline().strip())
    nums = list(map(int, sys.stdin.readline().split()))
    length = len(nums)
    sum1 = sum(nums)
    dfs(0, 0, 0)
    print(res)

2020/08/21 滴滴笔试题

8.小明希望你能够编写一个程序,输入一个正整数n,然后逐行逐列输出斐波那契蛇形矩阵中的元素。 

样例输入 

样例输出 
34 21 13 
1   1   8 
2   3   5 

思路:先用数组求出所需的斐波拉且数列,然后按照蛇形进行排列,这里和顺时针打印数组稍有不同,顺时针打印数组可以利用list队列特性,按照规律,pop()出相应的元素,将二维的矩阵变成了一维的输出数组。但本题可以借鉴该题的其它思路,当然前面提到的pop()肯定不可以,先构建一个蛇形矩阵的框架,然后按照蛇形(顺时针)的方式去找到框架中的元素,然后将赋值斐波拉且数列赋值进去。

def func(n):
    if n<2:
        print(n)
        return
    a,b = 0,1
    fab = [1]
    for i in range(n*n-1):
        fab.append(a+b)
        a,b = b,a+b
    temp = fab[::-1]
    # 得到斐波拉且数列后,构建框架,按照蛇形将数列赋值进去
    res = [[0]*n for _ in range(n)]
    l,r,t,b,m = 0,n-1,0,n-1,0 # 蛇形输出时的四边的记录变量left,right,top,bottom

    # 按照蛇形的方式,→,↓,←,↑,出现t>b or l>r就break,结束死循环。
    while True:
        for i in range(l,r+1): # →
            res[t][i] = temp[m]
            m+=1
        t+=1 # 按照规则加一
        if t>b:break
        for i in range(t,b+1):
            res[i][r] = temp[m]
            m+=1
        r-=1
        if l>r:break
        for i in range(r,l-1,-1):
            res[b][i] = temp[m]
            m+=1
        b-=1
        if t>b:break
        for i in range(b,t-1,-1):
            res[i][l] = temp[m]
            m+=1
        l+=1
        if l>r:break
    print(res)


if __name__ == "__main__":
    n = int(input())
    func(n)

9.输入字符矩阵,判断其中“CHINA”个数。该题使用递归的方法。注意回溯,方法中采用标志数组记录当前匹配过的状态。

def test02(matrix, china, rows, cols):
    tmp = [True] * rows * cols # 设立标志符,如果查找到匹配的就设为Flase。
    count = 0
    for i in range(rows):
        for j in range(cols):
            res = find(matrix, tmp, rows, cols, i, j, china)#对每个二维矩阵中的元素进行尝试
            count += res
    print(count // 4) # 在递归中匹配完成后还要进入一次递归发现path为空,返回一,此时四个都返回
  
def find(matrix, tmp, rows, cols, i, j, path):
    count = 0
    if not path:
        return 1 # 搜索完成就返回1
    index = i * cols + j
    if i < 0 or i >= rows or j < 0 or j >= cols \
            or matrix[index] != path[0] or not tmp[index]: # 首字符不匹配,或者刚被匹配过
        return 0

    tmp[index] = False # 没有执行if的return,则说明匹配
    # 进行递归下,向当前位置的四周搜索,抛开当前字符,匹配剩下的
    count = find(matrix, tmp, rows, cols, i - 1, j, path[1:]) + \
            find(matrix, tmp, rows, cols, i + 1, j, path[1:]) + \
            find(matrix, tmp, rows, cols, i, j - 1, path[1:]) + \
            find(matrix, tmp, rows, cols, i, j + 1, path[1:])
    tmp[index] = True # 进行回溯
    return count
 
 
if __name__ == '__main__':
    n = int(input().strip())
    matrix = []
    for i in range(n):
        matrix.extend(input().strip()) # extend()将一个整体list或str,按照元素加入。
    test02(matrix, "CHINA", n, n) # 将字符矩阵变成一维,变成一个字典形式

10.分词结果修改。输入:第一行是分好词的一句话(字符串),词与词间由空格分开,但存在一些问题;第二行是若干个需要匹配的词,词与词间有空格分开。输出:修改后的分词结果(一个字符串),词与词间由空格分开。

思路:遍历存在分词结果错误的字符串s,遇到空格直接复制到res,遇到字符进行匹配尝试,尝试当前字符与关键字中每个的首字母是否相同,相同继续匹配该关键字剩下的部分与遍历时当前字符后面的部分,遇到后面部分为空格跳过,设置标志位显示是否匹配成功。匹配成功将该关键之加入res并向前更新index,匹配失败则直接复制遍历时的当前字符。总之,暴力遍历,按照常规思维完成操作。

s = input().strip() # 存在问题的分词结果,存为一个字符串
temp = input().strip().split(" ") # 需要匹配的词
res = ""
index = 0
while index < len(s): # 遍历存在问题的分词结果,如果碰到空格复制给res,碰到字符进行判断
    flag = 0
    if s[index] == " ":
        res += " "
        index += 1
        continue
    for i in range(len(temp)): # 遍历碰到字符,则进行匹配
        if s[index] == temp[i][0]: # 遍历时,当前字符和temp中的某个词的首字母相同,进一步匹配
            t_index = index 
            g = 0 # 匹配是否成功的标志,1为失败
            for j in range(len(temp[i])):
                if s[t_index] == " ": t_index += 1 # 进一步匹配,遇到空格跳过
                if s[t_index] == temp[i][j]:
                    t_index += 1
                else:
                    g = 1
                    break
            if g == 0:
                flag = 1 # 匹配成功
                index = t_index # 匹配成功,向前更新index
                if res[-1] == " ":
                    res += temp[i]+" "
                else:
                    res += " "+temp[i]+" "
                break
    if flag == 0: # 匹配失败,直接复制
        res += s[index]
        index += 1
print(res)

11.牛牛的背包问题:牛牛的背包容量为w。牛牛家里一共有n袋零食, 第i袋零食体积为v[i]。牛牛想知道在总体积不超过背包容量的情况下,他一共有多少种零食放法(总体积为0也算一种放法)。第一行输入n和w,第二行输入n个v[i]。

思路:背包问题,一般使用dp和递归。

N,W = list(map(int,input().split()))
V = list(map(int, input().split()))

def count(W, V):
    if W<=0:return 1
    if len(V)<=0:return 1
    if sum(V)<=W: # v中的物品都可以放进去。
        return 2**len(V) # v中的每个物品有两种情况
    if V[0]<=W: # 开始递归
        return count(W-V[0], V[1:]) + count(W, V[1:]) # 放或者不放
    else:
        return count(W, V[1:]) # 放不下
print(count(W,V))

阿里笔试题:

12.x∈[1,A],y∈[1,B],x和y都是整数,找出这样的两个整数 x/y = a/b​,且xy乘积最大。系统输入A,B,a,b。

思路:使用暴力的两重循环或者一重循环在资源限制下无法通过,只有使用数学的方法来去求解。首先需要对a/b进行约分,找到他们的最大公约数,使用辗转相除法。化简后为a1,b1,由于要使得x/y = a1/b1,即x = ka1,y = kb1,此时a1,b1已知,需要找到满足条件得最大的k,用A去除以a取整求得A中能得到的的最大k1,用B去除以b取整求得B中能得到的最大k2,为了相满足在范围内,取min(k1,k2)得到k。求出x,y。O(1)&O(1)

def compute(m, n):
    if not n:
        return m
    else:
        return compute(n, m % n)
def get_max(m,n,a,b):
    num = compute(a,b)
    a //= num
    b //= num

    k = min(n//b,m//a)
    x = k*a
    y = k*b
    return str(x)+' '+str(y)
temp = list(map(int,input().split()))
if len(temp) == 4 :
    s = get_max(temp[0],temp[1],temp[2],temp[3])
    print(s)

2020/08/28 京东笔试题

13.一个三角形二维数组,小球从最上层中间进入,每次可以向下、左下或右下移动一格,求小球滚到最底部时最大的积分总和。

算法疑难杂题_第1张图片

接受到这个数组,用strip()去掉前后的空格,发现每次移动,横坐标要么不变,要么+1,要么+2,而纵坐标显然每次向下移动+1

计算之前要先判断j, j-1, j-2是否越界,只计算不越界的。

def method(nums):
    if nums==0:
        return 0
    game = [] 
    for _ in range(nums):
        game.append(list(map(int, input().strip().split())))

    for i in range(1, nums):
        for j in range(len(game[i])):
            item_1 = game[i-1][j] if 0<=j<(2*i-1) else 0 # 过滤越界的
            item_2 = game[i-1][j-1] if 0<=(j-1)<(2*i-1) else 0
            item_3 = game[i-1][j-2] if 0<=(j-2)<(2*i-1) else 0
            game[i][j] += max((item_1,item_2,item_3)) # 状态转移方程
    return max(game[-1]) # 自上而下,找到最后一行最大的


if __name__ == '__main__':
    nums = int(input().strip())
    res = method(nums)
    print(res)

2020/09/05 B站

14.现有N条鱼,A数组记录每条鱼的体积,每轮执行一次大鱼吃小鱼,对于每条鱼,它在每次操作时会吃掉右边比自己小的第一条鱼,在一次操作中,每条鱼吃比自己小的鱼的时间是同时发生的,比如[5,4,3],一次操作后,剩下[5]。求最多的可以操作的次数。

n = eval(input())
fish = list(map(int, input().split()))
fish.append("#") # 在尾部增加哨兵
stack, cnt, pre = [], 0, len(fish) # stack存储可以一组可以大鱼吃小鱼的鱼,pre用于判断是否变化
while fish:
    if fish[0] == "#": # 一次操作后,首位为哨兵
        fish.append(fish.pop(0)) # 将哨兵放到后面去
        if len(fish) == pre:
            break
        else:
            pre = len(fish)
            cnt += 1

    stack.append(fish.pop(0)) 
    while fish[0] != "#" and int(stack[-1]) > int(fish[0]): # 将小鱼放入stack
        stack.append(fish.pop(0))
    fish.append(stack.pop(0)) # 提取stack中吃完后的大鱼到fish中,准备下一次操作。
    while stack: # 清空stack,其他鱼被吃掉。
        stack.pop()
print(cnt)

15.寻找公共最长子串,动态规划,dp[i][j]为分别以s1[i],s2[j]结尾的相同子串的长度,额外添加一行一列“0”方便初始化。求出相同子串的长度和结尾的位置,然后在s1中去切片出来。

str1 = input()
str2 = input()
def func(s1,s2):
    res = ''
    s = 0 # 记录最长的长度
    p = 0 # 记录最长长度的结尾位置
    dp = [[0 for _ in range(len(s2)+1)]for _ in range(len(s1)+1)] # 1.定义好状态 
    i = 0
    while is: # 寻找最长长度和位置
                    s = dp[i][j]
                    p = i
            else:
                dp[i][j] = 0
            j+=1
        i+=1
    i = p - s # 逆向求出起始位置
    while i 

寻找最长公共子序列,与最长公共子串的区别是,不需要保证相同字符的连续性。同样也是使用dp。

"""
"helloworld" "loop"->3
"""
s1 = list(input())
s2 = list(input())

dp = [[0 for _ in range(len(s1)+1)]for _ in range(len(s2)+1)] # 状态转移矩阵初始化
for i in range(1,len(s2)+1): 
    for j in range(1,len(s1)+1):
        if s2[i-1]==s1[j-1]: # dp中存储的是以i,j为结尾的字符串中最长的公共子序列的长度 
            dp[i][j] = dp[i-1][j-1]+1
        else:
            dp[i][j] = max(dp[i-1][j],dp[i][j-1]) # 状态转移方程


print(dp[-1][-1])

16.斜对角线打印矩阵,按照副对角线的走向打印。

"""
1 3 6
2 5 9
4 8 11
7 10 12   m = 4, n = 3 
可以看出每条对角线的起始元素的横坐标变化:先增大,翻转后一直为m-1。纵坐标先一直为0,反转后增大。
"""
m,n =  map(int,input().split())
res = [[0 for _ in range(n)] for _ in range(m)]
s = 1
def func(res):
    start = 0 # 记录翻转时刻,如在7的位置,即m-1,后面每条对角线的起始元素的横坐标都为m-1
    global s
    for j in range(n): # 控制输出下半部分,列值一直增大。
        for i in range(start,m): # 此时纵坐标为0,行值一直增大,控制上半部分
            i1,j1 = i,j 
            while i1 >= 0 and j1<=n-1: # 打印每条对角线上的元素
                res[i1][j1] = s
                s +=1
                i1-=1
                j1+=1
        if i == m-1 and j == 0: # 纵坐标为0下,横坐标增大到m-1翻转后面i一直定于m-1
            start = m-1
    return res
func(res)
print(res)

17.走出迷宫最少的步数,使用dfs。

maze=[[1,1,1,1,1,1,1,1,1],
[1,0,0,1,0,0,1,0,1],
[1,0,0,1,1,0,0,0,1],
[1,0,1,0,1,1,0,1,1],
[1,0,0,0,0,1,0,0,1],
[1,1,0,1,0,1,0,0,1],
[1,1,0,1,0,1,0,0,1],
[1,1,0,1,0,0,0,0,1],
[1,1,1,1,1,1,1,1,1]] # 迷宫,1为障碍
a,b,c,d = 3,1,5,7 # 起点与终点坐标
s,m = 0,float('inf') # s缓存步数,m缓存最小值。

def dfs(x,y,s):
    global m
    if maze[x][y] :return  #  递归终止条件
    if x==c and y==d:
        m = min(s,m)
        return

    s+=1
    maze[x][y] = 1 # 递归处理

    dfs(x+1,y,s) # 继续递归
    dfs(x,y+1,s)
    dfs(x-1,y,s)
    dfs(x,y-1,s)

    maze[x][y]=0 # 此问题需要回溯

dfs(a,b,s)
print(m)

18.输入n组特征进行排列组合,注意输出的顺序。时间O(nm),后一特征在前一特征被遍历完后才变化。DFS

算法疑难杂题_第2张图片

n = int(input())
tmp,res = [],[]
for _ in range(n):
    list1 = list(input().split())
    tmp.append(list1) # 接收特征

cur = [] # DFS搜索,缓存结果,根据观察,最后一个特征变化较慢,第一个特征变化较快。从后往前搜索。

def dfs(cur,i):
    if i == -1: # 搜索到第一个特征
        a = []
        a = a + cur
        res.append(a) # 由于append(cur)是浅拷贝,cur后面还会变化,故用a来获取cur值
        return
    for j in range(len(tmp[i])): 
        cur.append(tmp[i][j]) # 压入cur栈
        dfs(cur,i-1)
        cur.pop() # 进行回溯
        
dfs(cur,n-1)    
for i in range(len(res)):
    print("-".join(res[i][::-1])) # 由于从后往前搜,res中的结果为倒序。

19.字符串中,长度大于1的子回文字符串的个数。dp。

s = input()
if len(s) <= 1:print(0)
n = len(s)
tmp = list(s)
dp = [[0 for _ in range(n)]for _ in range(n)] # dp数组,起始位置和结束位置所组成字符串状态
sum = 0
for i in range(n): # 将单个字符标记为是
    dp[i][i] = 1
for j in range(1,n): # j为结束位置,i为起始位置
    for i in range(0,j):
        if s[i] == s[j] and (i == j-1 or dp[i+1][j-1]): # 上一状态或者已经相邻
            dp[i][j] =1
            sum +=1
print(dp)
print(sum)

20.检查图是否为有向无环图,可以使用bfs,dfs。课程表安排图。BFS:如果按照BFS遍历,遍历一次入度减1,还是存在入度不为0,则存在环。DFS:按照DFS遍历,被遍历过的课程置为-1,本轮访问的置为1,初始化为0,再次访问本轮访问的点,即为有环。

from collections import deque # 引入双端队列作记录预先课程学完,可以学习的课程。

class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        indegrees = [0 for _ in range(numCourses)] # 入度表,记录每门课需要预学习的课程数量
        adjacency = [[] for _ in range(numCourses)] # 图的邻接表,记录连接
        queue = deque()
        
        for cur, pre in prerequisites: # cur为当前课程,pre为需要预学习的课程
            indegrees[cur] += 1 # 
            adjacency[pre].append(cur) 
        
        for i in range(len(indegrees)): # 让入度为0的课程进入代表可以学习的queue
            if not indegrees[i]: queue.append(i)
        
        while queue:
            pre = queue.popleft() # 代表pre这门课程入度为0,已经学习了
            numCourses -= 1 
            for cur in adjacency[pre]: 
                indegrees[cur] -= 1
                if not indegrees[cur]: queue.append(cur) 
        return not numCourses 

21.全排列,时间O(n*n!),递归 

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        """
        1.递归这种题一定要把问题简单化,不能复杂化,用两位或三位的nums想问题。
        2.全排列是位与位交换的问题。
        3.如1,2,3,4. 先把1固定,递归地求2,3,4的全排列,又把2固定,递归地求3,4的全排列……直到剩一个数,输出这个排列
        """
        def func(first):
            if first == n: # 递归终结的情况,处理,下一步递归,回溯
                res.append(nums[:])
            for i in range(first,n): 
                nums[first],nums[i] = nums[i],nums[first] # 假设只有两位或者三位的nums;
                func(first+1)
                nums[first],nums[i] = nums[i],nums[first]

        n = len(nums)
        res = []
        first = 0
        func(first)
        return res

22.零钱兑换,动态规划。时间O(Sn),S为金额,n是零钱面额数量。空间O(S)。

class Solution:
    def coinChange(self, coins: List[int], amount: int) -> int:
        dp = [float('inf')] * (amount + 1) # dp数组存储相应金额所需的最少硬币数量
        dp[0] = 0 # 0元所需0个硬币

        for i in range(1,amount+1): # 填满dp每个状态
            for j in coins: # 每个状态下遍历零钱面额,寻找最少的情况
                if j <= i:  # 面额小于当前状态金额时
                    dp[i]=min(dp[i],dp[i-j]+1) # 初始为"inf"
        if dp[amount] == float('inf'): #如果目标金额没有改变,说明无法兑换。
            return -1
        else:
            return dp[amount]

23.求图的连通子图的个数并保存每个子图的节点:https://www.cnblogs.com/sunupo/p/13510172.html

24.字符串是否由子串拼接,遍历每一种情况

"""
abcabc->abc;
"""
def copy(s):
    if len(s)<2:
        return s
    res = ""
    for i in range(1,len(s)//2+1): # 寻找能拼接的子串,最长为长度的一半
        ss = s[:i] # 遍历所有情况
        if is_copy(ss,s):
            res = ss
    return res if res else 'false'
 
def is_copy(ss,s):
    i = len(s)//len(ss)
    if len(s)%len(ss) != 0:
        return False
    if ss*i == s: # 由i次重复构成
        return True
    else:
        return False
     
s = input()
print(copy(s))

25.整数乘积最大化,动态规划,dp数组中存储当前情况下最大的乘积。

"""
10=3+3+4->36
"""
def main():
    n = int(input())
    dp = [0 for _ in range(n + 1)]
    dp[0] = 0
    dp[1] = 0
    dp[2] = 1
 
    for i in range(2, n + 1):
        for j in range(2, i):
            dp[i] = max(dp[i], j * max(dp[i - j], i - j))
    return dp[n]
 
print(main())

26.奖牌榜排序,输入信息为国家名和金银铜奖牌数量,按照相应奖牌数量等排序。

import sys
M = int(input()) # number
if M>=31 or M<=0:
    print("参赛国家数量不正确,范围应在(0,31)中!")
    sys.exit()
country = []
flag = 0
for i in range(M):
    temp = list(input().split())
    if len(temp)!=4:
        print("输入参赛国奖牌信息有误!应包含国名,金银铜牌数量四个信息。")
        flag = 1
        break
    if len(temp[0])>20:
        print("输入的国家名称数量超过了20个字符!")
        flag = 1
        break
    for j in range(3):
        if not(int(temp[j+1]) in range(0,201)):
            print("输入的奖牌数量不正确!")
            flag = 1
    if flag:break
    country.append(temp)
# 按国家首字母排序
country.sort(key=lambda x: x[0])
# 按铜牌数量排序
country.sort(key=lambda x: int(x[3]), reverse=True)
# 按银牌数量排序
country.sort(key=lambda x: int(x[2]), reverse=True)
# 按金牌数量排序
country.sort(key=lambda x: int(x[1]), reverse=True)
if flag == 0:
    for i in range(M):
        print(country[i][0])

 

你可能感兴趣的:(学习问题,算法)