目录
特殊日期
记一个日期为yy年mm月dd 日,统计从2000年1月1日(含)到2000000年1月1日(含),有多少个日期满足年份yy是月份mm的倍数,同时也是dd的倍数。
题目链接
典型的使用枚举思想,可以从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 个,问有多少种不同的分法。 只要有其中一个小朋友在两种方案中分到的糖果不完全相同,这两种方案就算作不同的方案。
题目链接
使用递归思想,对于每个孩子使用两个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
题目链接
这里有个技巧就是每次三个国家都会增加,我们只需要计算相对增加量,然后对三个国家各自胜利分类讨论,因为我们可以控制哪些事件先发生哪些事件后发生,所以最后只需要使用贪心算法的思想,相对增量越少的时间越先发生就能使用最多的事件数
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),请问代价和最少为多少。
题目链接
输入格式
输入的第—行包含—个正整数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)
子树的大小
题目链接
输入格式
输入包含多组询问。输入的第一行包含—个整数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)