[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)

目录

  • 前言
  • A.卡片
  • B.直线
  • C.货物摆放
  • D.路径
  • E.回路计数
  • F.时间显示
  • G.杨辉三角(下图是C++的图)
  • H.左孩子右兄弟
  • I.异或数列(图不是python组的)

前言

A.卡片

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第1张图片
思路:

代码:

def get_cot(num):
    """
    :param num: 要拼的数字
    :return: 组成该数字各位数的个数
    """
    cot_dict = {
     }
    while num != 0:
        tail = num % 10
        if tail not in cot_dict:
            cot_dict[tail] = 1
        else:
            cot_dict[tail] += 1
        num //= 10
    return cot_dict

if __name__ == '__main__':
    have_dict = {
     }
    for i in range(10):
        tmp_cot = int(input(f"{i}有几张?"))
        have_dict[i] = tmp_cot
    now_num = 0
    break_flag = 0
    while True:
        if break_flag == 1:
            break
        now_num += 1
        cot_dict = get_cot(now_num)
        for key in cot_dict:
            cot = cot_dict[key]
            if have_dict[key] >= cot:
                have_dict[key] -= cot
            else:
                break_flag = 1
                break
    print(now_num - 1) # 因为now_num不够了,所以break了,所以最多now_num-1

答案:

>>>0有几张? 2021
>>>1有几张? 2021
>>>2有几张? 2021
>>>3有几张? 2021
>>>4有几张? 2021
>>>5有几张? 2021
>>>6有几张? 2021
>>>7有几张? 2021
>>>8有几张? 2021
>>>9有几张? 2021
>>>3181

B.直线

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第2张图片
思路:

代码:

def get_k_b(dot1, dot2):
    """
    :param dot1: 第一个点(当前遍历到的)
    :param dot2: 第二个点(之前的)
    :return: (k,b) if x1 - x2 != 0 else ('inf',x1)
    """
    x1, y1 = dot1
    x2, y2 = dot2
    k, b = None, None
    if x1 == x2:
        k = float('inf')
        b = x1 # k不存在时,通过b来确定唯一直线
    else:
        '''
        y1 = kx1 + b 
        y2 = kx2 + b
        ∴ k = (y1-y2) / (x1-x2)   b = (x1y2-x2y1) / (x1-x2)
        '''
        k = (y1-y2) / (x1-x2)
        b = (x1 * y2 - x2 * y1) / (x1 - x2)
    return (k, b)


if __name__== '__main__':
    X_cot, Y_cot = map(int, input().split())
    dot_lst = []
    k_b_lst = []
    ans = 0
    # 初始化第0行
    for x in range(X_cot):
        dot_lst.append((x, 0))
    k_b_lst.append((0,0))
    ans += 1
    # 从第1行开始
    for x in range(X_cot):
        for y in range(1, Y_cot):
            now_dot = (x, y)
            for pre_dot in dot_lst:
                k_b = get_k_b(now_dot, pre_dot)
                if k_b not in k_b_lst:
                    k_b_lst.append(k_b)
                    ans += 1
            dot_lst.append(now_dot)
    print(ans)

答案:

20 21
40257

C.货物摆放

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第3张图片
思路1:
DP
代码:

import math
def get_fac(num):
    """
    :param num: 待求因数的数
    :return: 因数列表
    """
    fac_num = []
    for i in range(1, int(math.sqrt(num))+1):
        if num % i == 0:
            fac_num.append(i)
            fac_num.append(num // i)
    return fac_num

if __name__ == '__main__':
    n = int(input())
    ### dp[i][j]
    ### i 代表货物数 ,j: 0 代表长; 1 代表宽; 2 代表高
    dp = [[ 0 for i in range(n + 1)] for j in range(3)]
    fac_dict = {
     } # 避免重复计算
    # 初始化:仅考虑长时,无论多少货物数,仅有1种方案
    n_fac_lst = get_fac(n)
    for n_fac in n_fac_lst:
        dp[0][n_fac] = 1
    # 从考虑宽开始
    for j in range(1, 3):
        for i in n_fac_lst:  # 这里没写for i in range(1, n+1),因为所有可能出现的i一定是n的因数
            i_fac_lst = get_fac(i)
            for fac in i_fac_lst:
                dp[j][i] += dp[j-1][i//fac]
    print(dp[2][n])

内存不够

思路2:
DP+空间压缩
代码2:

import math
def get_fac(num):
    """
    :param num: 待求因数的数
    :return: 因数列表
    """
    fac_num = []
    for i in range(1, int(math.sqrt(num))+1):
        if num % i == 0:
            fac_num.append(i)
            fac_num.append(num // i)
    return fac_num

if __name__ == '__main__':
    n = int(input())
    ### dp[i][j]
    ### i 代表货物数 ,j: 0 代表长; 1 代表宽; 2 代表高
    dp = [0 for i in range(n + 1)]
    fac_dict = {
     } # 避免重复计算
    # 初始化:仅考虑长时,无论多少货物数,仅有1种方案
    n_fac_lst = get_fac(n)
    for n_fac in n_fac_lst:
        dp[n_fac] = 1
    # 从考虑宽开始
    for j in range(1, 3):
        for i in n_fac_lst[::-1]: # 从后往前遍历,否则上一行的值会被更新
            i_fac_lst = get_fac(i)
            for fac in i_fac_lst[1:]: # 因数1不需要,因为dp[i]本身就是
                dp[i] += dp[i//fac]
    print(dp[n])

内存还是不够…用dp存似乎不合适

思路3:
暴力。求因数的函数更新了一下…原来没有用math.sqrt…
代码:

import math
def get_fac(num):
    """
    :param num: 待求因数的数
    :return: 因数列表
    """
    fac_num = []
    for i in range(1, int(math.sqrt(num))+1):
        if num % i == 0:
            fac_num.append(i)
            fac_num.append(num // i)
    return fac_num

if __name__ == '__main__':
    ans = 0
    n = int(input())
    n_fac_lst = get_fac(n)
    for i in n_fac_lst:
        for j in n_fac_lst:
            for k in n_fac_lst:
                if i * j * k == n:
                    ans += 1
    print(ans)

答案:

2021041820210418
2430

D.路径

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第4张图片
思路:
迪杰斯特拉
代码:

def get_LCM(a, b):
    """
    :param a: 数a
    :param b: 数b
    :return:  最小公倍数
    """
    large, small = None, None
    if a > b:
        large = a
        small = b
    else:
        large = b
        small = a
    k = 1
    while True:
        if (small * k) % large == 0:
            return small * k
        k += 1

def get_nb_node(now_node, num):
    """
    :param now_node: 当前节点
    :param num: 总节点数
    :return:  当前节点的邻接节点
    """
    nb_lst = []
    for k in range(-1, -22, -1):
        nb_node = now_node + k
        if nb_node < 1:
            break
        nb_lst.append(nb_node)
    for k in range(1, 22):
        nb_node = now_node + k
        if nb_node > num:
            break
        nb_lst.append(nb_node)
    return nb_lst

def build_graph(num):
    """
    :param num: 总节点树
    :return: 邻接矩阵
    """
    graph = [ [float('inf') for i in range(num + 1)] for j in range(num + 1)]
    for now_node in range(1, num + 1):
        neighbour_node_lst = get_nb_node(now_node, num)
        for nb_node in neighbour_node_lst:
            weight = get_LCM(now_node, nb_node)
            graph[now_node][nb_node] = graph[nb_node][now_node] = weight
    return graph

def dijkstra(start, end):
    """
    :param start: 开始节点
    :param end:  结束节点
    :return: 最短距离
    """
    num = len(graph) # true node num + 1
    nearest_dis_lst = graph[start][::]
    vis_lst = [0 for i in range(num)]
    vis_lst[start] = 1
    while True:
        nearest_dis = float('inf')
        nearest_node = None
        for node in range(1, num):
            if vis_lst[node] == 0:
                if nearest_dis_lst[node] < nearest_dis:
                    nearest_dis = nearest_dis_lst[node]
                    nearest_node = node
        if nearest_node == end:
            return nearest_dis
        if nearest_node == None:
            return -1
        vis_lst[nearest_node] = 1
        for nb_node, nb_dis in enumerate(graph[nearest_node]):
            if vis_lst[nb_node] == 0:
                nearest_dis_lst[nb_node] = min(nearest_dis_lst[nb_node], nearest_dis + nb_dis)
    return -1

if __name__ == '__main__':
    node_num = int(input())
    graph = build_graph(node_num)
    print(dijkstra(start = 1, end = node_num))

答案:

2021
10266837

E.回路计数

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第5张图片
思路:
不会,有空补一份递归的代码(比赛时没有运行出来)
emmm我的ACM大佬朋友劝我不要研究了(???),他的思路是状压DP

F.时间显示

思路:

代码:

def get_time(second):
    """
    :param second: 描述
    :return: 时、分、秒
    """
    second %= (24 * 3600)
    h = second // 3600
    second %= 3600
    m = second // 60
    second %= 60
    s = second
    return h, m, s

if __name__ == '__main__':
    s = int(input()) // 1000
    hour, minute, second = get_time(s)
    print("{0:0>2d}:{1:0>2d}:{2:0>2d}".format(hour, minute, second))

结果:

46800999
13:00:00

G.杨辉三角(下图是C++的图)


思路1:
一行一行模拟
代码:

答案:
肯定过不了

思路2:
每一行(从0开始)的最大值是组合数C_{layer}^{layer//2},所以想先确定行,再确定在该行的哪一个位置
代码:

def get_fac(start, end):
    """
    :param start: 大数
    :param end: 小数
    :return: start * start -1 * .... end
    """
    if (start, end) not in fac_dict:
        ans = 1
        for i in range(start, end - 1, -1):
            ans *= i
        fac_dict[(start, end)] = ans
    return fac_dict[(start, end)]

def get_C(sub, super):
    """
    :param sub: C的下标
    :param super: C的上标
    :return: C_{sub}^{super},排列组合数
    """
    return get_fac(sub, super + 1) // get_fac(super, 1)


def get_max(layer):
    """
    :param layer: 层数
    :return: 层的最大数,也就是 C_{layer}^{layer//2}
    """
    sub = layer
    super = layer // 2
    return get_C(sub, super)

def get_layer(num):
    """
    :param num: 输入的数
    :return: 最早出现在哪一层
    """
    layer = 0
    while True:
        l_max = get_max(layer)
        if l_max >= num:
            return layer
        layer += 1
    return -1


if __name__ == '__main__':
    num = int(input())
    fac_dict = {
     }
    layer = get_layer(num)
    # 之前层有多少个数字
    pre_cot = (1 + layer) * layer // 2
    # num在当前层处于哪一个位置
    sub = layer
    super = layer // 2
    break_flag = 0
    while True:
        if break_flag == 1:
            break
        C = get_C(sub, super)
        if C == num:
            break_flag = 1
            break
        super -= 1
    cot = pre_cot + super + 1
    print(cot)

答案:
应该是算了太多次阶乘,比思路1还慢…

思路3:
参考求杨辉三角中元素首次出现位置【2021蓝桥杯】
代码:

def get_fac(start, end):
    """
    :param start: 大数
    :param end: 小数
    :return: start * start -1 * .... end
    """
    if (start, end) not in fac_dict:
        ans = 1
        for i in range(start, end - 1, -1):
            ans *= i
        fac_dict[(start, end)] = ans
    return fac_dict[(start, end)]

def get_C(sub, super):
    """
    :param sub: C的下标
    :param super: C的上标
    :return: C_{sub}^{super},排列组合数
    """
    return get_fac(sub, super + 1) // get_fac(super, 1)

def get_index(pos, layer):
    """
    :param pos: 当前行的位置
    :param layer:  所在行
    :return: 总的位置
    """
    return (1 + layer) * layer // 2 + pos + 1



if __name__ == '__main__':
    num = int(input())
    fac_dict = {
     }
    # 内层行
    index = 1
    for pos in range(16, 1, -1):
        if index != 1:
            break
        layer = 2 * pos
        while True:
            C = get_C(layer, pos)
            if C > num:
                break
            elif C == num:
                index = get_index(pos, layer)
                break
            layer += 1
    # 外层行
    if index == 1:
        if num != 1:
            index = get_index(pos=1, layer=num)
    print(index)

答案:

1000000000
500000000500000002

H.左孩子右兄弟

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第6张图片

[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第7张图片
思路:
DFS,对于当前的node,首先查看node的子节点有多少个,假设有n个,则以node为根的二叉树的高度最少有n。接着计算所有以node的子节点为根的二叉树的高度,选择高度最高的那一个节点放到最下面,从而使高度最大化,即,遍历node的子节点,进行递归,得到子节点的最大高度,再加上原来的n,就是答案。
代码:

def dfs(node):
    """
    :param node: 根节点
    :return: 根节点所构造的二叉树的最大高度
    """
    n = len(child_lst[node]) # 至少为n
    child_height = 0
    for child_node in child_lst[node]:
        child_height = max(child_height, dfs(child_node))
    return n + child_height

if __name__ == '__main__':
    N = int(input())
    child_lst = [[] for i in range(N + 1)]
    for i in range(2, N + 1):
        parent = int(input())
        child_lst[parent].append(i)
    print(dfs(1))

答案:

5
1
1
1
2
4

I.异或数列(图不是python组的)

依稀记得题中的a和b初始化是0?(当时没做到这儿,印象不深)
[蓝桥杯]——2021蓝桥杯python组省赛复盘(暂7)_第8张图片
思路:
DP,但怎么表示状态呢?差不多的题目在力扣刷到过,但它的每一步只能从数列头或者尾选一个数字,那样的话就可以创建一个二维的dp,dp[i][j]表示i…j的数列,来做。这题中,每一步可以从剩下的数字中任意选取一个,不太知道怎么保存状态。

你可能感兴趣的:(蓝桥杯)