0
点赞
收藏
分享

微信扫一扫

第十二届蓝桥杯软件类省赛python组

我是小瘦子哟 2022-04-14 阅读 72

1.卡片

本题总分:5分 【问题描述】 小蓝有很多数字卡片,每张卡片上都是数字0到9

小蓝准备用这些卡片来拼一些数,他想从1开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。

小蓝想知道自己能从1拼到多少。

例如,当小蓝有30张卡片,其中0到9各3张,则小蓝可以拼出1到10,但是拼11时卡片1已经只有一张了,不够拼出11

现在小蓝手里有0到9的卡片各2021张,共20210张,请问小蓝可以从1拼到多少?

提示:建议使用计算机编程解决问题。

常规做法

li = [2021 for i in range(10)]
ans = 1
while True:
    tmp = ans
    while tmp // 10:
        if li[tmp % 10] == 0:
            break
        li[tmp % 10] -= 1
        print(li)
        tmp //= 10
    if li[tmp % 10] == 0:  # 当tmp为个位数时不能进入上述循环
        break
    li[tmp % 10] -= 1
    print(li)
    ans += 1
print(ans - 1)

使用functions.Counter计数

from collections import Counter
def func():
    li = [2021 for i in range(10)]
    ans = 1
    while True:
        for k, v in Counter(str(ans)).items():
            li[int(k)] -= int(v)
            print(li)
            if li[int(k)] < 0:
                return ans - 1
        ans += 1
if __name__ == '__main__':
    print(func())

2.直线

本题总分:5分 【问题描述】 在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。

给定平面上2×3个整点( x , y )0 ≤ x < 2 , 0 ≤ y < 3 , x ∈ Z , y ∈ Z, 即横坐标是0到1(包含0和1)之间的整数、纵坐标是0到2(包含0和2)之间的整数的点。这些点一共确定了11条不同的直线。

给定平面上20×21个整点( x , y )0 ≤ x < 20 , 0 ≤ y < 21 , x ∈ Z , y ∈ Z,即横坐标是0到19(包含0和19)之间的整数、纵坐标是0到20(包含0和20)之间的整数的点。请问这些点一共确定了多少条不同的直线。

求每条直线的斜率和截距,然后进行去重,不知道有没有更好的方法。

'''
斜率:k = (y2 - y1) / (x2 - x1)
截距:b = - k * x1 + y1 = (x2 * y1 - x1 * y2) / (x2 - x1)
'''
x, y = map(int, input().split())
points = [[i, j] for i in range(x) for j in range(y)]  # 每个点的坐标
line = set()  # 用来存储每条线的斜率和截距
for i in range(len(points) - 1):
    x1, y1 = points[i][0], points[i][1]
    for j in range(i, len(points)):
        x2, y2 = points[j][0], points[j][1]
        if x1 == x2:  # 当斜率为无穷时不进行计算,斜率为无穷时直线个数为x
            continue
        k = (y2 - y1) / (x2 - x1)
        b = (x2 * y1 - x1 * y2) / (x2 - x1)
        if (k, b) not in line:
            line.add((k, b))  # 利用元组不可变的性质,可以直接存入集合中
print(len(line) + x)  # 加上斜率为无穷时的直线个数x

3.货物摆放

本题总分:10分 【问题描述】 小蓝有一个超大的仓库,可以摆放很多货物。

现在,小蓝有n箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。

小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆L、W、H的货物,满足n=L×W×H

给定n,请问有多少种堆放货物的方案满足要求。

例如,当n=4时,有以下6种方案:1×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×1

请问,当n=2021041820210418(注意有16位数字)时,总共有多少种方案?

提示:建议使用计算机编程解决问题。

n = int(input())
line = set()  # 存储可被n整除的边长
for i in range(1, int(pow(n, 1 / 2)) + 1):
    if n % i == 0:  # i能被n整除时i和n-i添加入list
        line.add(i)
        line.add(n // i)
ans = 0
for l in line:
    for w in line:
        for h in line:
            if l * w * h == n:
                ans += 1
print(ans)

 

4.路径

本题总分:10分 【问题描述】

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。

小蓝的图由2021个结点组成,依次编号1至2021

对于两个不同的结点a,b,如果a和b的差的绝对值大于21,则两个结点之间没有边相连;如果a和b的差的绝对值小于等于21,则两个点之间有一条长度为a和b的最小公倍数的无向边相连。

例如:结点1和结点23之间没有边相连;结点3和结点24之间有一条无向边,长度为24;结点15和结点25之间有一条无向边,长度为75.

请计算,结点1和结点2021之间的最短路径长度是多少。

提示:建议使用计算机编程解决问题。

import math

def gcm(x, y: int) -&gt; int:  # 求最小公倍数
    return x * y // math.gcd(x, y)  # gcd求最大公约数

n = int(input())

dp = [float('inf')] * (n + 1)
dp[1] = 0
for i in range(1, n + 1):
    for j in range(i + 1, i + 22):
        if j &gt; n:
            break
        dp[j] = min(dp[j], dp[i] + gcm(i, j))
print(dp[n])

 

5.回路计算

本题总分:15分 【问题描述】

蓝桥学院由21栋教学楼组成,教学楼编号1到21。对于两栋教学楼a和b,当a和b互质时,a和b之间有一条走廊直接相连,两个方向皆可通行,否则没有直接连接的走廊。

小蓝现在在第一栋教学楼,他想要访问每栋教学楼正好一次,最终回到第一栋教学楼(即走一条哈密尔顿回路),请问他有多少种不同的访问方案?两个访问方案不同是指存在某个i,小蓝在两个访问方法中访问完教学楼i后访问了不同的教学楼。

提示:建议使用计算机编程解决问题。

递归——太慢跑不出来 

 

import math
import sys
sys.setrecursionlimit(10000)
n = int(input())
li = [i for i in range(2, n + 1)]
def dfs(li, li_tmp):
    global count
    if len(li_tmp) == len(li):
        count += 1
        return
    for j in range(len(li)):
        tmp = li_tmp[:]  # 进行浅拷贝,不然后面回不去
        if li[j] not in li_tmp and math.gcd(li_tmp[-1], li[j]) == 1:
            li_tmp.append(li[j])
            dfs(li, li_tmp)
            li_tmp = tmp[:]
count = 0
for i in range(len(li)):
    li_tmp = [li[i]]
    dfs(li, li_tmp)
print(count)

状态压缩DP——yyds 

 

from math import gcd
n = int(input())
m = 1 &lt;&lt; n
dp = [[0 for j in range(n)] for i in range(m)]  # dp[i][j]对于状态i,i的二进制表示中为1的位置 表示走过了教学楼j
load = [[False for j in range(n)] for i in range(n)]  # 存储i, j之间是否有路
for i in range(1, n + 1):
    for j in range(1, n + 1):
        if gcd(i, j) == 1:
            load[i - 1][j - 1] = True
dp[1][0] = 1
for i in range(1, m):  # 枚举每一种状态
    for j in range(n):
        if i &gt;&gt; j &amp; 1:  # 判断状态i是否包含第j栋教学楼
            for k in range(n):  # 枚举所有可能从教学楼k走到教学楼j的情况
                if i - (1 &lt;&lt; j) &gt;&gt; k &amp; 1 and load[k][j]:  # 判断状态i除去j后是否包含k
                    dp[i][j] += dp[i - (1 &lt;&lt; j)][k]
print(sum(dp[m - 1]) - dp[m - 1][0])

6.时间显示

时间限制:1.0s内存限制:256.0MB本题总分:15分

【问题描述】

小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从1970年1月1日00:00:00到当前时刻经过的毫秒数。

现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。

给定一个用整数表示的时间,请将这个时间对应的时分秒输出。

【输入格式】

输入一行包含一个整数,表示时间。

【输出格式】

输出时分秒表示的当前时间,格式形如HH:MM:SS,其中H表示时,值为0到23,MM表示分,值为0到59,SS表示秒,值为0到59时、分、秒不足两位时补前导0

[样例输入1]

46800999

[样例输出1]

13:00:00

[样例输入2]

1618708103123

[样例输出2]

01:08:23

python 内置函数解法

import time
n = int(input())
new_n = time.asctime(time.gmtime(n // 1000))
print(new_n[11:19])

算法解法 

n = int(input())
n //= 1000
day = 24 * 60 * 60
n %= day
s = n % 60
n //= 60
m = n % 60
n //= 60
h = n
print("{:02d}:{:02d}:{:02d}".format(h,m,s))

7.杨辉三角

时间限制:1.0s内存限制:256.0MB本题总分:20分

【问题描述】

下面的图形是著名的杨辉三角形:

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:

1,1,1,1,2,1,1,3,3,1,1,4,6,4,1,

给定一个正整数N,请你输出数列中第一次出现N是在第几个数?

【输入格式】

输入一个整数N.

【输出格式】

输出一个整数代表答案。

【样例输入】

6

【样例输出】

13

一维数组法

def find_n(n):
    if n == 1:
        return 1
    res = 3  # 已计算过的个数
    li, l = [1, 2], 3  # 将要进行比对的行的元素及其行数
    while n not in li:
        res += len(li) * 2 - l % 2
        li, l = [1] + [li[i] + li[i + 1] for i in range(len(li) - 1)] + ([li[-1] * 2] if l % 2 == 0 else []), l + 1
    return res + li.index(n) + 1
if __name__ == '__main__':
    n = int(input())
    print(find_n(n))

数学规律法 

如图所示:

def C(a, b):  # 计算组合值
    res = 1
    tmp = a
    for j in range(1, b + 1):
        res = res * tmp // j
        if res &gt; n:
            return res
        tmp -= 1
    return res
def check(k):
    l, r = k * 2, n
    while l &lt; r:
        mid = l + r &gt;&gt; 1
        if C(mid, k) &gt;= n:
            r = mid
        else:
            l = mid + 1
    if C(r, k) != n:
        return False
    print(r * (r + 1) // 2 + k + 1)
    return True


n = int(input())
if n == 1:
 print(1)
else:
 k = 16
 while not check(k):
     k -= 1

 


def func():
 """
 :dp:前i个括号序列中左括号比右括号多j的方案的序列的集合
 :i:前i个括号序列
 :j:左括号比右括号多j个
 :return:前n个括号序列中左括号比右括号多的方案的最小值
 """
    dp = [[0 for i in range(n + 2)] for i in range(n + 1)]
    dp[0][0] = 1
    for i in range(1, n + 1):
        if s[i - 1] == '(':
            for j in range(1, n + 1):
                dp[i][j] = dp[i - 1][j - 1]
        else:
            dp[i][0] = dp[i - 1][0] + dp[i - 1][1]
            for j in range(1, n + 1):
                dp[i][j] = dp[i][j - 1] + dp[i - 1][j + 1]
    for i in range(n + 1):  # 前n个括号序列中最小的
        if dp[n][i]:
            return dp[n][i]


s = list(input())
n = len(s)
mod = 10 ** 9 + 7
left = func()  # 从左看计算
s.reverse()  # 从右往左看括号序列,发现可将原序列反转并将'('和')'互换
for i in range(n):
    if s[i] == ')':
        s[i] = '('
    else:
        s[i] = ')'
right = func()  # 从右看计算
print(left * right % mod)

 

 

 

 

 

 

举报

相关推荐

0 条评论