2023年pythonB组省赛题解

1:2023

思路

只能想到纯纯暴力,枚举每一个数,统计每个数是否包含2023。

注意要算的是完全不包含2023的数目,别算反了。

代码

答案:85959030

# 完全不包含2023
def judge(x):
    li = [3,2,0,2]; i=0
    while x>0:
        t = x%10; x//=10
        if t==li[i]: i+=1
        if i==4: return 0
    return 1

if __name__=='__main__':
    st = 12345678; ed = 98765432
    res = 0
    for x in range(st, ed+1):
        res += judge(x)
    print(res) # 85959030

2:硬币兑换

思路

能暴力出结果,但是我考虑错了。

我们要计算的是,任意次兑换后,某种硬币的数目达到最大。那么容易想到,只有把所有能兑换过去的硬币全部兑换过去,得到的目标硬币数才可能最大。

假设我们的目标硬币是面值为5(奇数),初始硬币数为5,并且面值分别为1+4、2+3的硬币可以兑换过来。假设目标硬币面值为4(偶数),初始硬币数为4,面值分别为1+3、2+2的硬币可以兑换过来。综上,我们可以枚举所有可能的目标硬币情况,对每一个面值的硬币分奇偶讨论他的最大数目,然后求出所有硬币的 max 值。

比赛时,我认为目标硬币面值的情况为1~2023,其实是想错了。应该是1–2023*2。要注意一点,当目标硬币的面值超过2023后,兑换前要考虑是不是coin1和coin2都有旧版硬币。

代码

答案:682425

def calcu(x):
    cnt = 0
    if x<=2023: cnt=x
    if x%2>0: # 奇数
        for i in range(1, x//2+1):
            if x-i<=2023: cnt += i
    else:
        for i in range(1, x//2):
            if x-i<=2023: cnt += i
        if x//2<=2023: cnt += x//4
    return cnt

if __name__=='__main__':
    res = 0
    for k in range(1,2023*2+1):
        res = max(res, calcu(k))
    print(res) # 682425

3:松散子序列

思路

这是一道线性dp题。考试时,我写了个二重循环(虽然也是dp的思想),估计只能过50%左右。

f[i]表示以a[i]结尾的子序列的最大价值,由于松散子序列要求pi - pi-1 >= 2,所以f[i] = max(f[j]) + a[i],j从0 ~ i-2。最后答案是遍历所有f[i],找一个最大值。

这样写是一个二重循环,肯定会超时,所以要优化代码。我们发现f[i-2]一定大于f[i-4],且f[i-3]一定大于f[i-5],因此内层循环可以转化成只比较f[i-2]f[i-3]的大小。

代码

if __name__=='__main__':
    s = input(); l = len(s)
    f = [0]*(l+5)
    for i in range(l):
        if i-2>=0: f[i] = max(f[i], f[i-2])
        if i-3>=0: f[i] = max(f[i], f[i-3])
        f[i] += ord(s[i])-ord('a')+1
    res = 0
    for i in range(l):
        res = max(res, f[i])
    print(res)

4:管道

思路

这是一道二分+区间合并的题目,但是鼠鼠想不出来,听了y哥的点拨写的。

二分法可以找到区间的边界点,如果我们以时间为轴,右侧区间的时间点满足管道中每一段中间的传感器都检测到有水流,左侧区间的时间点是管道中每一段中间的传感器不能都检测到水流,所以本题的要求就是求出右区间的边界点。二分法有很多需要注意的细节,所以我就直接背模板了。

在二分法里,对于每一个时间点,我们都要判断此时是否满足区间性质。可以算出该时间点,每一个阀门喷出的水能覆盖多长的区间。用区间合并算法合并这些区间,然后判断最后得到的区间是不是能覆盖所有的传感器。

要注意这题在区间合并部分不需要排序,如果加上排序,区间合并部分的时间复杂度就是 O ( n l o g n ) O(nlogn) O(nlogn),在new online judge上测会有一个样例超时。如果不排序的话,区间合并在模板的基础上就要改,不能有区间数目cnt的统计情况。因为题目里说 L i − 1 < L i L_{i-1}<L_i Li1<Li,所以是可以证明不需要排序的。但鼠鼠就不写了。

代码

# 二分+区间合并
def check(t, l):
    # 区间合并
    le = len(li); ed = 0
    for i in range(le):
        if t-si[i]>=0:
            a = max(li[i]-t+si[i], 1); b = min(li[i]+t-si[i], l)
            if a<=ed+1:
                ed = max(ed, b)

    if ed==l: return True
    else: return False

if __name__=='__main__':
    n, ll = map(int, input().split())
    li, si = [], []
    for _ in range(n):
        li_, si_ = map(int, input().split())
        li.append(li_); si.append(si_)
    # 二分
    l, r = 0, int(2e9)
    while l<r:
        mid = (l+r)//2;
        if check(mid, ll): r=mid
        else: l=mid+1
    print(l)

5:保险箱

思路

想了个bfs的写法,但是只过了一个样例。网上也找不到题解,放弃了。

6:树上选点

思路

以为自己省赛写对了,结果发现只过了一个样例。或许省赛我只有10多分吧。

后记

国赛考完了,以后有机会再填坑。

  • 25
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值