【python语言】第十三届蓝桥杯国赛 c/c++b组

一、2022.

1. 考点:背包问题
2. 难度:⭐⭐⭐
3. 要点分析:

可以将此题看成一个背包装物品,只能装10件物品,且重量恰好为2022.

这次的“背包问题”额外增加了对物品的数量限制,“二维dp”可能较难处理;

因此升级为“三维dp”,多一个维度来记录物品的数量。

dp[i][j][k] 表示在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”,会超时。

③寻找上一张时间最接近的票据,

dp[i]=max(dp[i-1],dp[k]+v[i])

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))
  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值