一、2022.
1. 考点:背包问题
2. 难度:⭐⭐⭐
3. 要点分析:
可以将此题看成一个背包装物品,只能装10件物品,且重量恰好为2022.
这次的“背包问题”额外增加了对物品的数量限制,“二维dp”可能较难处理;
因此升级为“三维dp”,多一个维度来记录物品的数量。
表示在1~i个体积的物品中, 选取了j个物品, 和为k,数量的多少。
"""索引作为推导过程 值作为种类"""
dp = [[[0]*2030 for _ in range(11)] for _ in range(2030)]
"""初始化:"在第1~i个体积的物品中, 选取了0个, 和为0"的情况为1"""
for i in range(2023):
dp[i][0][0] = 1
for i in range(1,2023):
for j in range(1,11):
for k in range(1,2023):
if k < i:
"""当前第i个物品的体积小于和k 只能照搬"""
dp[i][j][k] = dp[i-1][j][k]
else:
"""
在这i个物品中,
选择当前第i个物品, 则dp[i-1][j-1][k-i]
不选择当前第i个物品, 则直接照搬dp[i-1][j][k]
"""
dp[i][j][k] = dp[i-1][j][k] + dp[i-1][j-1][k-i]
print(dp[2022][10][2022])
二、钟表。
1. 考点:模拟
2. 难度:⭐⭐
3. 要点分析:无。
for i in range(6+1): #时针
for j in range(60): #分针
for k in range(60): #秒针
A=abs(30*(i+j/60+k/3600)-6*(j+k/60))
B=abs(6*(j+k/60)-6*k)
if A>180:
A = 360-A
if B>180:
B = 360-B
if A==2*B and (i,j,k)!=(0,0,0):
print(i,j,k)
sys.exit(0)
三、卡牌。
1. 考点:二分
2. 难度:⭐⭐⭐
3. 要点分析:
①将待查找序列设置为“能凑成的套数”,并通过二分找到该值。
②判断mid套是否满足条件。若满足条件每个牌位都能通过补牌 / 不补牌凑到;如果凑不到,则不满足条件。
下面来讨论一下关于“二分”的基本步骤和模板代码(仅用于有序序列)
二分的难点有两处
(1)二分边界,即L和R的退出条件
(2)check函数的函数体实现——验证是否成立二分的模板有两个
(1)确定边界值在左半边的情况,将区间分为[l,mid-1]和[mid,r]
while(l<r){ int mid=l+r+1>>1; if(check(mid)){ l=mid; }else{ r=mid-1; } } return l;
(2)确定边界值在右半边的情况,将区间分为[l,mid]和[mid+1,r]
while(l<r){ int mid=l+r+1>>1; if(check(mid)){ l=mid; }else{ r=mid-1; } } return l;
import datetime
import math
import sys
n, m = map(int, input().split())
a = list(map(int,input().split()))
b = list(map(int,input().split()))
def check(mid):
buf = m
for i in range(n):
if a[i] >= mid:
continue
if a[i]+b[i]<mid or mid-a[i]>buf:
return 0
buf -= (mid-a[i])
return 1
"""将能凑成的套数设置为有序序列 并通过二分找到该值"""
l, r = 0, 2*n
while l<r:
mid = (l+r+1)//2
if check(mid):
l = mid
else:
r = mid-1
print(l)
四、最大数字。
1. 考点:DFS
2. 难度:⭐⭐⭐⭐
3. 要点分析:相比较其它“dfs”的暴力求解,此题需要稍微动一下脑子。
一开始,我将此题理解为了需要“进位”和“退位”,于是这道题就被我人为复杂化了(不需要)。
高位的数字越大,整体越大;于是我们从高位开始考虑。
①对于1号操作,需要不断增加至9,(除了9到0外)任何一个“增加”都起正作用。由此,将某位上的数字增加至9后,若有剩余,留给下一位。
②对于2号操作,需要不断减少至9;若无法达到9,任何一个“减少”都是起负作用。由此,若2号操作的次数不足以减少至9,则不使用。
import datetime
import math
import sys
n, a, b = map(int, input().split())
nums = [int(t) for t in str(n)]
res = 0
def dfs(i,v,a,b):
global res
if i<len(nums):
x = nums[i]
# 一号操作
k = min(a,9-x)
dfs(i+1,v*10+x+k,a-k,b)
# 二号操作
if b>=x+1:
dfs(i+1,v*10+9,a,b-x-1)
else:
res = max(res,v)
dfs(0,0,a,b)
print(res)
五、出差。
1. 考点:枚举
2. 难度:⭐
3. 要点分析:无。
print("A C")
print("A D")
print("B D")
六、费用报销。
1. 考点:动态规划(背包问题)
2. 难度:⭐⭐⭐⭐
3. 要点分析:“01背包”的变形问题
①注意日期的限制条件,将所有日期都转换为天数;根据日期的大小对票据进行排序。
②结合题意,寻找”最大值“,因此只需要设置“一维dp”即可;若设置“二维dp”,会超时。
③寻找上一张时间最接近的票据,
import datetime
N, M, K = map(int, input().split())
dayV = []
tmp_days = datetime.datetime(2001,1,1) #随意取一个非闰年的年份
for i in range(N):
month, day, v = map(int, input().split())
# 将日期转化为天数
current_date = datetime.datetime(2001,month,day)
days = (current_date-tmp_days).days+1
dayV.append([days, v])
# 对日期进行排序
dayV.sort(key=lambda dayV:dayV[0])
dayV = [[0,0]] + dayV
dp = [0]*(N+1)
dp[1] = dayV[1][1]
for i in range(2,N+1):
# 寻找上一张时间最接近的票据
k = i-1
while k>0:
if dayV[i][0]-dayV[k][0] >= K:
break
k -= 1
# 判断是否不大于最大金额M
if dp[k]+dayV[i][1]<=M:
dp[i] = max(dp[i-1], dp[k]+dayV[i][1])
print(max(dp))