Python【2021 Blue Bridge Cup Provincial Competition Fill in the Blank】

Python【2021 Blue Bridge Cup Provincial Competition Fill in the Blank】

A. Cards

insert image description here

note = [2021 for i in range(0,10)]
i, flag = 1, 1
while flag:
    now = i
    while now:
        if note[now % 10] == 0:
            # 当第i张卡片需要的数字不够时,说明总共能拼到i-1
            print(i - 1)
            flag = 0
            break
        note[now % 10] -= 1
        now //= 10
    i += 1

Answer : 3181

B. Straight line

insert image description here

# 直线可以用k(斜率)和b(与y轴的截距)唯一确定,因此可以看有多少个不同的(k,b)对
import pickle

s = set()
# 插入20 * 21个点
point = []
for i in range(0,20):
    for j in range(0,21):
        point.append([i,j])

for i in range(0,len(point) - 1):
    for j in range(i + 1,len(point)):
        x1,y1 = point[i]
        x2,y2 = point[j]
        # 横竖直线不考虑
        if x1 == x2 or y1 == y2:
            continue
        k = (y2 - y1) / (x2 - x1)
        # y = kx + b
        # b = y - kx
        #   = y1 - k x1
        #   = (y1*x2 - y1*x1 - y2*x1 + y1*x1) / (x2 - x1)
        #   = (y1 * x2 - y2 * x1) / (x2 - x1)
        b = (y1 * x2 - y2 * x1) / (x2 - x1)
        if (k,b) not in s:
            s.add((k,b))
print(len(s) + 41)

Answer : 40257

C. cargo placement

insert image description here

import math

n = 2021041820210418
x = []
# 长宽高都应该是n的因子,所以先找到n的因子
for i in range(1,int(math.sqrt(n)) + 1):
    if n % i == 0:
        x.append(i)
        x.append(n // i)
ans = 0
# 枚举因子
for i in x:
    for j in x:
        for k in x:
            if i * j * k == n:
                ans += 1
print(ans)

Answer : 2430

D. Path

insert image description here

import math

# 存储图
mp = [[math.inf] * 2025 for i in range(0,2025)]
# 存储点1到每个点的距离
dis = [math.inf for i in range(0,2025)]
dis[1] = 0
# 记录某个点是否被标记过
note = [False for i in range(0,2025)]
# 初始化mp
for i in range(1,2022):
    for j in range(1,2022):
        if math.fabs(i - j) <= 21:
            mp[i][j] = mp[j][i] = i * j // math.gcd(i,j)
# dijkstra
while 1:
    mn, k = math.inf, 0
    for j in range(1,2022):
        if (not note[j]) and dis[j] < mn:
            mn, k = dis[j], j
    if k == 0:
        break
    note[k] = True
    for j in range(1,2022):
        dis[j] = min(dis[j],dis[k] + mp[k][j])
print(dis[2021])

Answer : 10266837

E. Loop count [state compression DP]

insert image description here

The idea is written in the comments.

import math

# 用0 ~ 20来表示第1 ~ 21栋楼
# 一共可能出现0 -- 1<<21 - 1个数
# dp[i][j]表示数字i这个状态以第j栋楼结尾的路径的条数
dp = [[0] * 25 for i in range(0,1 << 21)]
# mp[i][j]用来表示i到j是否有路径
mp = [[False] * 22 for i in range(0,22)]
for i in range(1,22):
    for j in range(1,22):
        if math.gcd(i,j) == 1:      # 互质说明有路径
            mp[i - 1][j - 1] = True
# 000000000000000000001 这是初始时刻在第1(0)栋教学楼,这是二进制表示的数字是1
# 所以初始化dp[1][0] = 1,表示1这个数能以第0栋楼结尾的路径总数是1
dp[1][0] = 1
# 遍历1 ~ 111111111111111111111,看每个数字能通往哪些数字
for i in range(1,1 << 21):
    # 遍历i的每一位,看是否已经走到过了第j位(第j栋楼)
    for j in range(0,21):
        # 没走到过
        if not (i & 1 << j):
            continue
        # 走到过则遍历第j栋楼能到达的楼
        for k in range(0,21):
            # 第k栋楼已经被访问过或者jk之间没边
            if (i & (1 << k)) or (not mp[j][k]):
                continue
            dp[i | (1 << k)][k] += dp[i][j]
print(sum(dp[(1 << 21) - 1]))

Answer : 881012367360

Guess you like

Origin blog.csdn.net/qq_45985728/article/details/123967706