2023蓝桥杯python大学A组部分题目详细解析

目录

特殊日期

分糖果

三国游戏

平均

翻转 

子树的大小


特殊日期

记一个日期为yy年mm月dd 日,统计从2000年1月1日(含)到2000000年1月1日(含),有多少个日期满足年份yy是月份mm的倍数,同时也是dd的倍数。

题目链接

https://www.lanqiao.cn/problems/3495/learning/?page=1&first_category_id=1&sort=students_count&category_id=3&name=%E7%89%B9%E6%AE%8A%E6%97%A5%E6%9C%9F

典型的使用枚举思想,可以从2000年1月1日(含)到2000000年1月1日(含)每天是否符合特殊日期的要求进行枚举,符合条件计数就加1

mouth1 = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]  # 平年每个月天数列表,这里为了与月份统一下标,所以第一个为0
mouth2 = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]  # 闰年每个月天数列表


def fun(x):  # 判断闰年函数
    if (x % 4 == 0 and x % 100 != 0) or x % 400 == 0:  # 能被4整除且不能被100整除,或者能被400整除
        return 1
    else:
        return 0


ans = 0
for i in range(2000, 2000000):
    for j in range(1, 13):  # 月份从1到12
        if fun(i) == 0:  # 如果是平年
            for m in range(1, mouth1[j] + 1):  # 日期从1到mouth[j]
                if i % j == 0 and i % m == 0:  # i表示年份,j表示月份,m表示日期
                    ans = ans + 1
        if fun(i) == 1:  # 如果是闰年
            for n in range(1, mouth2[j] + 1):
                if i % j == 0 and i % n == 0:
                    ans = ans + 1
print(ans + 1)# 由于2000000.1.1以上循环没有但是也满足,所以要加上
# 35813063

这里使用定义每个月份的天数的列表可以简化后面的代码,这个思想对做这种时间题很有帮助

遇到时间问题还是一种思想就是可以使用datetime类,是python非常好用的用于计算时间的自带类,该类的使用方法可以查看我上次发的资源

https://download.csdn.net/download/m0_62574258/88760682

例如下题

对于一个日期,我们可以计算出年份的各个数位上的数字之和,也可以分别计算月和日的各位数字之和。请问从1900年1月1日至9999年12月31日,年份的数位数字之和等于月的数位数字之和加日的数位数字之和。例如,2022年11月13日满足要求,因为2+0+2 +2 =(1+1)+(1+3)。请提交满足条件的日期的总数量。

import datetime


def fun(x):
    return sum(int(i) for i in str(x))  # 定义fun函数,用于计算数字的和


# 定义开始和结束日期范围
d1 = datetime.datetime(1900, 1, 1)
d2 = datetime.datetime(9999, 12, 31)
# 计算开始和结束日期之间的总天数
y = (d2 - d1).days
ans = 0

# 遍历日期范围内的每一天
for i in range(y):
    # 从当前日期中提取年、月和日
    a = d1.year
    b = d1.month
    c = d1.day

    # 检查年份的数字和是否等于月份数字和加上日期数字和
    if fun(a) == fun(b) + fun(c):
        ans = ans + 1

    # 使用一天的时间增量移动到下一天
    d1 = d1 + datetime.timedelta(days=1)

# 打印结果
print(ans)

分糖果

两种糖果分别有 9 个和 16 个,要全部分给 7 个小朋友,每个小朋友得到的糖果总数最少为 2 个最多为 5 个,问有多少种不同的分法。 只要有其中一个小朋友在两种方案中分到的糖果不完全相同,这两种方案就算作不同的方案。

题目链接

https://www.lanqiao.cn/problems/4124/learning/?page=1&first_category_id=1&sort=students_count&category_id=3&name=%E5%88%86%E7%B3%96%E6%9E%9C

使用递归思想,对于每个孩子使用两个for循环分配两种糖果的个数,当一个孩子分配完时再递归分配下一个孩子,此时糖果数量要对应的扣除上次分配出去的,如果7个孩子都分配完且糖果没有剩余,则算一种方案

ans = 0


def dfs(child, n, m):
    global ans  # 先声明ans是全局变量,否则函数内部的操作变量ans不会影响到函数外的ans
    if child == 0: # 如果孩子分配完就退出函数
        if n == 0 and m == 0: #如果孩子分配完且糖果也刚好分配完方案数加1
            ans += 1
        return
    for i in range(n + 1):  # 第一种糖果
        for j in range(m + 1):  # 第二种糖果
            if 2 <= i + j <= 5:
                dfs(child - 1, n - i, m - j)  # 继续递归对下一个孩子进行分配


dfs(7, 9, 16)  # 7个孩子,第一种糖果有9个,第二种糖果有16个
print(ans)
#5067671

三国游戏

小蓝正在玩一款游戏。游戏中魏(X)、蜀(Y)、吴(Z)三个国家各自拥有一定数量的士兵X,Y,Z(一开始可以认为都为0)。游戏有n个可能会发生的事件,每个事件之间相互独立且最多只会发生一次,当第i个事件发生时会分别让X,Y ,Z增加Ai,Bi,Ci,当游戏结束时(所有事件的发生与否已经确定),如果X,y,Z的其中一个大于另外两个之和,我们认为其获胜。例如,当X >Y +Z时,我们认为魏国获胜。小蓝想知道游戏结束时如果有其中一个国家获胜,最多发生了多少个事件?如果不存在任何能让某国获胜的情况,请输出-1。

输入格式

输入的第一行包含一个整数n。

第二行包含n个整表示Ai,相邻整数之间使用一个空格分隔。

第三行包含n个整数表示Bi,相邻整数之间使用一个空格分隔。

第四行包含n个整数表示Ci,相邻数之间使用一个空格分隔。

样例输入

3
1 2 2
2 3 2
1 0 7

样例输出:2

题目链接

https://www.lanqiao.cn/problems/3518/learning/?page=1&first_category_id=1&sort=students_count&category_id=3&name=%E4%B8%89%E5%9B%BD%E6%B8%B8%E6%88%8F

这里有个技巧就是每次三个国家都会增加,我们只需要计算相对增加量,然后对三个国家各自胜利分类讨论,因为我们可以控制哪些事件先发生哪些事件后发生,所以最后只需要使用贪心算法的思想,相对增量越少的时间越先发生就能使用最多的事件数

n = int(input())
a = list(map(int, input().split()))
b = list(map(int, input().split()))
c = list(map(int, input().split()))
x = []
y = []
z = []
for i in range(n):
    x.append(a[i] - b[i] - c[i])  # 存储每个事件魏国相比较其它两国的相对增量
    y.append(b[i] - a[i] - c[i])  # 存储每个事件蜀国国相比较其它两国的相对增量
    z.append(c[i] - a[i] - b[i])  # 存储每个事件吴国相比较其它两国的相对增量
    # 由于三国一开始都是0,所以只要当所有发生事件的相对增量和大于0即胜利
    # 将相对增量排序,要求最大发生事件数,应该让相对增量为正数全部发生,然后相对增量为负数的从大到小发生,直到相对增量和大于0
    # 可以认为所有事件按贡献的大小从大到小的顺序发生,所以将相对增量降序排序
    x.sort(reverse=True)
    y.sort(reverse=True)
    z.sort(reverse=True)


def win(p):  # 求p国获胜的最大发生事件数
    if p[0]<=0:    # 相对增量最大的都小于等于0,说明p国无法获胜
      return -1
    sum = 0
    for i in range(n):
        sum = sum + p[i]    # 从最大的开始加,直到sum为负数时说明此时事件数已经最多了 
        if sum < 0:
            return i
    return len(p)    # 所有事件都执行完还是大于0,直接返回全部事件数


print(max(win(x), win(y), win(z)))  # 依次求x国、y国、z国胜利的最大发生事件数,然后再取最大值

平均

有一个长度为n的数组(n是10的倍数),每个数a都是区间[0,9]中的整数。小明发现数组里每种数出现的次数不太平均,而更改第i个数的代价为bi,他想更改若干个数的值使得这10种数出现的次数相等(都等于n/10),请问代价和最少为多少。

题目链接

https://www.lanqiao.cn/problems/3532/learning/?page=1&first_category_id=1&sort=students_count&category_id=3&name=%E5%B9%B3%E5%9D%87

输入格式

输入的第—行包含—个正整数n。

接下来n行,第i行包含两个整数ai,bi,用一个空格分隔。

样例输入

10
1 1
1 2
1 3
2 4
2 5
2 6
3 7
3 8
3 9
4 10

样例输出:27

还是使用贪心思想,从0到9开始遍历,看哪个数字的数量超过了n/10,某个数字超过平均个数多少个就要改多少个,而改哪几个是由代价决定的,代价越小越先被改变。这里使用二维列表,b列表中存放了10个一维列表,0到9每种数字的所有代价分别存在这10个一维列表

n = int(input())
sum = 0  # 代价和
b = [[] for i in range(10)]  # 创建二维列表,b[i]存储i数字的所有代价
for i in range(n):
    o, e = map(int, input().split())  # o是数字几,e是该数的代价
    b[o].append(e)  # 根据数字找到对应哪个列表,然后把该数的代价加入到该列表中

ave = n // 10
for i in range(10):  # 数字从0到9遍历
    if len(b[i]) > ave:  # len(b[i])表示i数字的个数,如果大于平均值,多出来的就要变成其他数
        num = len(b[i]) - ave  # 与平均值作差,差值num表示有多少个变成别的数
        b[i].sort()  # 将i数字的所有代价升序排序
        for j in range(num):  # 从中取代价最小的num个数,从开头开始取,一直取num个
            sum = sum + b[i][j]  # 把取出的数需要的代价加上sum

print(sum)

翻转 

小蓝用黑白棋的n个棋子排成了一行,他在脑海里想象出了一个长度为n的01串T,他发现如果把黑棋当做1,白棋当做0,这一行棋子也是一个长度为n的01串S。小蓝决定,如果在S中发现一个棋子和它两边的棋子都不—样,就可以将其翻转变成另一个颜色。也就是说,如果S中存在子串101或者010,就可以选择将其分别变为111和000,这样的操作可以无限重复。
小蓝想知道最少翻转多少次可以把S变成和T一模一样。
输入包含多组数据。
输入的第一行包含一个正整数d表示数据组数。

对于每组数据,输出—行包含一个整数,表示答案,如果答案不存在请输出-1。

样例输入

2
1000111
1010101
01000
11000

样例输出

2
-1

题目链接

https://www.lanqiao.cn/problems/3520/learning/?problem_list_id=19&page=3&sort=students_count
由于有两个01字符串,它们要相等即它们的异或值要全为0,可以先将s和t进行异或,然后遍历异或值看是否存在010,如果存在,把1变为0并且计数器加1,如果最后还存在1则不能把s变为t

d = int(input())
for _ in range(d):
    ans = 0
    t = list(input())
    s = list(input())
    x = []  # x存储t和s异或的结果
    for i in range(len(s)):
        x.append(int(t[i]) ^ int(s[i]))
    for j in range(len(x) - 2):
        if x[j] == 0 and x[j + 1] == 1 and x[j + 2] == 0:  # 遍历寻找010子串
            ans = ans + 1
            x[j + 1] = 0
    if sum(x) != 0:
        print(-1)
    else:
        print(ans)

子树的大小

题目链接

https://www.lanqiao.cn/problems/3526/learning/?page=1&first_category_id=1&sort=students_count&category_id=3&name=%E5%AD%90%E6%A0%91%E7%9A%84%E5%A4%A7%E5%B0%8F

输入格式
输入包含多组询问。输入的第一行包含—个整数T,表示询问次数。接下来T行,每行包含三个整数n,m,k,表示—组询问。
输出格式
输出T行,每行包含一个整数表示对应询问的答案。

样例输入

3
1 2 1
11 3 4
74 5 3

样例输出

1
2
24

本题需要用一些完全m叉树的性质,具体总结可以看我这篇博客https://mp.csdn.net/mp_blog/creation/editor/137544689

这里使用两种方法

方法一:用dfs递归实现,能过50%的样例还不错,毕竟简单暴力

t=int(input())  # t个询问

def dfs(n, m, k):  # 一共n个节点,m叉树,结点k
    global ans
    if k > n:
        return
    for i in range(1, m + 1):  # i表示当前k结点的第i个孩子
        if k * m + (i - (m - 1)) <= n:  # k * m + (i - (m - 1))为k的第i个孩子结点编号,如果没超过最大节点的编号
            ans = ans + 1  # 个数加1
            dfs(n, m, k * m + (i - (m - 1)))  # 遍历该孩子




for _ in range(t):
  ans=1 # 一开始从1开始,因为自己本身算一个结点
  n, m, k = map(int, input().split())
  dfs(n, m, k)
  print(ans)

方法二:公式求解,高效,但是要对m叉树的性质很熟悉

from math import *

t = int(input())
for _ in range(t):
    n, m, k = map(int, input().split())
    dk = ceil(log(k*(m-1), m))
    d = ceil(log(n*(m-1), m))
    last = n - (m ** (d - 1) - 1) // (m - 1)  # last等于结点数n减去前d-1层结点数
    num = k - (m ** (dk - 1) - 1) // (m - 1)  # num等于当前结点编号减去前dk-1层结点数
    res1 = (m ** (d - dk) - 1) // (m - 1)
    if last - (num - 1) * m ** (d - dk) < 0:
        res2 = 0
    else:
        res2 = min(m ** (d - dk), last - (num - 1) * m ** (d - dk))
    print(res1 + res2)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Genius256

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值