14届蓝桥杯Python B组国赛题解

写在前面

在这里插入图片描述

题解全由我本人实际刷题后写的代码,题解能够保证提交后一定正确无误,若有更优解可以在评论区@我,不胜感激。

A 弹珠堆放

这是一个填空题,直接写答案的。

而且非常的简单,如果弹珠目前是第n层,那么最底部会比它的上面一层多n个弹珠。

写一个小模拟就可以得到答案了。

cnt = 1
res = 1
for i in range(2, 500):
    cnt += i
    res += cnt
    if res > 20230610:
        print(i, res)
        break
# 494
print(494)

B 划分

题目大意就是将40个数分成两组数的和相乘,我们需要先知道一个数学定理,就是这两组数的差值越小它们乘积越大。

所以我们的初步想法就是给这40个数分组的时候要均匀分配,具体的解决方案我们可以定一个dp数组,先对40个数求和然后取一半的值作为一个背包的容量,然后就转换成了一个类似背包的问题,dp数组的值只能为0或1,代表能否取到该值。具体细节参考代码。

cnt1, cnt2 = 0, 0
a = [5160, 9191, 6410, 4657, 7492, 1531, 8854, 1253, 4520, 9231, 1266, 
     4801, 3484, 4323, 5070, 1789, 2744, 5959, 9426, 4433, 4404, 5291, 
     2470, 8533, 7608, 2935, 8922, 5273, 8364, 8819, 7374, 8077, 5336, 
     8495, 5602, 6553, 3548, 5267, 9150, 3309]

N = sum(a) // 2 + 1
dp = [0] * N
dp[0] = 1

for i in range(40):
    for j in range(N):
        dp[j] = max(dp[j - a[i]], dp[j])

tag = 0
for i in range(N-1, -1, -1):
    if dp[i] == 1:
        tag = i
        break

print(tag * (sum(a) - tag))
print(12873625444)

C 偶串

这道编程题比较简单,不知道国赛考这个是不是怕我们爆零。

用一个字典统计每个字母出现的个数,然后再遍历,如果有出现奇数个次数的字母就输出NO

d = {}
a = list(input())
for i in a:
    if i not in d:
        d[i] = 1
    else: d[i] += 1
    
flag = True
for i in d.values():
    if i&1: flag = False
    
if flag: print('YES')
else: print('NO')

D 交易账本

我承认上一道编程题我说话声音比较大,这一道题的题面也太长了吧,这不纯纯考我们的阅读理解吗?

我自己第一次做的时候做的我心烦气躁,但是认真读完题目,按照要求来模拟还是挺好做的。

这题还需要注意几个关键信息,比如我们要记录每次输出的编号次序以及金额,然后输入的时候要把对应输出的金额全部用上。

N = 1000 + 10
for _ in range(int(input())):
    n, m = map(int, input().split())
    flag = True # 账本合理性的标记
    outMessge = [[0] * N for _ in range(N)] # 记录交易记录的输出金额
    for id in range(m):
        op = list(map(int, input().split()))
        # 输入部分
        inMoney = 0
        inCount = op[0]
        for i in range(1, 2 * inCount + 1, 2):
            fromId, fromOutNumber = op[i], op[i + 1]
            if fromId > id: # 输入来自于未来的输出,不合理
                flag = False
            if fromId != -1:
                inMoney += outMessge[fromId][fromOutNumber]
                outMessge[fromId][fromOutNumber] = 0 # 清空输入中的金额
        # 输出部分 
        outCount = op[2 * inCount + 1]
        outNum, outMoney = 0, 0
        for i in range(2 * inCount + 2, 2 * inCount + 2 * outCount + 2, 2):
            acount, val = op[i], op[i + 1]
            outMessge[id][outNum] += val # 记录输出的信息
            outMoney += val
            outNum += 1
        if fromId != -1 and inMoney != outMoney: # 输出的金额不等于输入的金额
            flag = False
            
    if flag: print('YES')
    else: print('NO')

E 背包问题

看到1000的数据量级,三重背包肯定是超空间的。观察数据量只有 c n t A cnt_A cntA c n t B cnt_B cntB比较小,所以可以枚举积木的数量,因此关键点在于枚举 A A A积木在背包1和背包的数量。

for _ in range(int(input())):
    B1, B2, B3 = map(int, input().split())
    cntA, cntB = map(int, input().split())
    vA, vB = map(int, input().split())
    if vA > vB: # 确保积木A的体积比B的体积小
        cntA, cntB = cntB, cntA
        vA, vB = vB, vA
    ans = 0
    for i in range(cntA + 1):
        if i * vA > B1:
            break
        for j in range(cntA - i + 1):
            if j * vA > B2:
                break
            
            res = i + j # res存已经放了多少积木数
            remainCntA = cntA - i - j
            remainCntB = cntB
            remain_B1 = B1 - vA * i
            remain_B2 = B2 - vA * j
            remain_B3 = B3
            
            # 将B放入背包1和背包2中
            res += min(remain_B1 // vB, cntB) # 将背包1的剩余空间用B积木尽量装
            remainCntB -= min(remain_B1 // vB, remainCntB)
            res += min(remain_B2 // vB, remainCntB)
            remainCntB -= min(remain_B2 // vB, remainCntB)
        
            # 剩下的背包3先用余下的A装
            res += min(remain_B3 // vA, remainCntA)
            remain_B3 -= min(remain_B3 // vA, remainCntA) * vA
            # 再用B补充余下的背包空间
            res += min(remain_B3 // vB, remainCntB)
            
            ans = max(ans, res)
    print(ans)

F 翻转

典型的动态规划题, d p i , j dp_{i,j} dpi,j表示处理到第 i i i个字符串,结尾是 j j j字母的最小长度。

def tfm(s, i): # a~z对应0~25
    return ord(s[i]) - ord('a')

n = int(input())
a = [0]
for i in range(n): a.append(input())

dp = [[0x3f3f3f3f] * 26 for _ in range(n + 1)]
dp[1][tfm(a[1], 0)] = dp[1][tfm(a[1], 1)] = 2
for i in range(2, n + 1):
    for j in range(26):
        A, B = tfm(a[i], 0), tfm(a[i], 1)
        # 不翻转 'AB'
        if A == j: dp[i][B] = min(dp[i][B], dp[i - 1][j] + 1)
        else: dp[i][B] = min(dp[i][B], dp[i - 1][j] + 2)
        # 翻转的情况 'BA'
        if B == j: dp[i][A] = min(dp[i][A], dp[i - 1][j] + 1)
        else: dp[i][A] = min(dp[i][A], dp[i - 1][j] + 2)
            
ans = 0x3f3f3f3f
for i in range(26):
    ans = min(ans, dp[n][i])
print(ans)

G 最大阶梯

这道题我第一思路是对整体阶梯图形做hash然后比对,发现很难实现。那就想了一个dfs去暴力,如果加一个记忆化就差不多是一个动态规划。

每一个阶梯图形都可以从一个底端点延申过来,我们对图像都考虑成下三角的形式,所以

d p i , j = m i n ( d p i + 1 , j , d p i , j + 1 ) + 1 dp_{i,j}=min(dp{i+1,j},dp_{i,j+1})+1 dpi,j=min(dpi+1,j,dpi,j+1)+1

能转移的前提是它们直接的颜色相同,否则就是1。

def update(G):
    global ans
    dp = [[1] * (h + 1) for _ in range(h + 1)]
    for i in range(h - 2, -1, -1):
        for j in range(h - 2, -1, -1):
            if G[i][j] == G[i + 1][j] == G[i][j + 1]:
                dp[i][j] = min(dp[i + 1][j], dp[i][j + 1]) + 1
            ans = max(ans, dp[i][j])
            
def turn(G):
    new_G = [[0] * h for _ in range(h)]
    for i in range(h):
        for j in range(h):
            new_G[j][h - i - 1] = G[i][j]
    return new_G

h = int(input())
G = [list(map(int, input().split())) for _ in range(h)]
ans = 0
update(G)
for _ in range(3):
    G = turn(G) # 让G顺时针转90°
    update(G)
print(ans)

H 最长回文前后缀

这题看似简单,其实算字符串 h a s h hash hash正与反的位置非常的烦,这个我用了一个贪心思路,结果错了,只能过50%。

def hash(s):
    n = len(s)
    h = [0] * n
    for i in range(1, n):
        h[i] = (h[i - 1] * seed + ord(s[i])) % mod
    return h 

def query(h, l, r):
    return (h[r] - h[l - 1] * pow(seed, r - l + 1)) % mod

seed = 233
mod = 10**9 + 7
s = list(input())
s1, s2 = [0] + s, [0] + s[::-1]
h1, h2 = hash(s1), hash(s2)

l, r = 0, len(s) + 1
while l + 1 != r:
    mid = (l + r) // 2
    if query(h1, 1, mid) == query(h2, 1, mid):
        l = mid
    else: r = mid

extra = 0
s3, s4 = [0] + s1[l+1:], [0] + s1[l+1:][::-1]
h3, h4 = hash(s3), hash(s4)
for i in range(1, len(s3)):
    if query(h3, 1, i) == query(h4, len(s3) - i, len(s3) - 1):
        extra = i
print(l * 2 + extra)

I 贸易航线

背包容量 k k k先可以不看,在途中如果一个商品的利润很大的话,我们把所有的背包容量去装那个利润很大商品即可。所以就相当于背包容量为1的时候,我们去解决问题,最最后将答案乘上 k k k即可。

因此 d p i , j dp_{i,j} dpi,j可表示为途径 i i i地背包里装的是 j j j物品时最大的收益, j j j为0的时代表当前背包为空。

状态转移方程如下:

  1. 到达 i i i地的时候,可以继承 i − 1 i-1 i1地的状态,即$dp_{i,j} = d p i − 1 , j dp_{i - 1,j} dpi1,j
  2. 如果 j j j商品在当地可以售卖, d p i , 0 = m a x ( d p i , 0 , d p i , j + g i , j ) dp_{i,0} = max(dp_{i,0}, dp_{i,j} + g_{i,j}) dpi,0=max(dpi,0,dpi,j+gi,j)
  3. 如果 j j j商品在当地可以进货, d p i , j = m a x ( d p i , j , d p i , 0 − g i , j ) dp_{i,j} = max(dp_{i,j}, dp_{i,0} - g_{i,j}) dpi,j=max(dpi,j,dpi,0gi,j)
dp = [[-10**18] * 11 for _ in range(10**5 + 1)]
g = [0] * (10**5 + 1)
n, m, k = map(int, input().split())
for i in range(1, n + 1):
    g[i] = [0] + list(map(int, input().split()))
dp[0][0] = 0
for i in range(1, n + 1):
    for j in range(0, m + 1):
        dp[i][j] = dp[i - 1][j]
    for j in range(1, m + 1):
        if g[i][j] != -1:
            dp[i][0] = max(dp[i][j] + g[i][j], dp[i][0])
    for j in range(1, m + 1):
        if g[i][j] != -1:
            dp[i][j] = max(dp[i][0] - g[i][j], dp[i][j])
print(dp[n][0] * k)

J 困局

待更新…

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@KevenDuan

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

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

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

打赏作者

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

抵扣说明:

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

余额充值