第十三届蓝桥杯省赛python A组个人解析
4280
2022.04.09
2022.04.09
发布于 未知归属地

首先不得不吐槽一下,这监考和咨询服务是真**的**
蓝桥杯这次绝对是厕所战神杯了,肯定还有一堆车队
既然这样,而且也不能退钱,我就随意打了,心态非常的好,好像系统有个bug会不停黄牌警告,我根本没管,题目我分析出样例有问题,直接自己就改了,懒得咨询。双机位监考的手机不停的有人找志愿者说这说那,也没管。好像外界的一切都影响不了我了,整场比赛居然还意外的享受。
下面来分享下我的解法,带思考过程带赛时代码
不保对,有问题可以讨论
A题
4+19+20*21=443
B题
似乎是中国剩余定理,但我不会写这个算法,赛时打表找的规律
我先随便选了个12,13,找满足他们余数的数,发现这些数都隔了一个恒定的数
既然这样,我固定第一个数,然后让他们每次跳这个恒定的数不就行了
先暴力找满足2-9的余数的数,以及恒定间隔数。
以这个数开始再暴力跳10-22,最后我看这个数够大了,直接让他跳最后答案了,跳23-49
共三次,跳出答案为:
2022040920220409
恰好是2022年04月09号重复两遍
如果他们不设这个答案我可能还会再检查一下的,毕竟这方法太诡异了
C题
质因数分解模板+一点点变动

n = int(input())
i = 2
ans = 0
while i * i <= n:
    if n % i == 0:
        ans += 1
        while n % i == 0:
            n //= i
    i += 1
if n != 1:
    ans += 1
print(ans)

D题
实质就是让边尽量贴在一起
比赛时多画一画,找找规律,防止漏情况
接下来就是分类讨论:
最理想的就是三个矩形有三条边都相同
那直接叠就行了,这种情况多边形四条边
image.png 次之的情况就是有两个矩形的两条边可以合成另一矩形的一条边,且合成的两个矩形除用来合成的边之外的两条边等长,也合成了一个大矩形,四条边。 若用来拼的两个矩形的另外两条边不相等,就会变成6条边image.png
再次之的情况就是只有两个矩形有两条边相同
那么就是这样,六条边
image.png
最烂的情况就如下,八条边
image.png
代码比赛时因为懒得想,打了最直接的一种,有点丑,懂个意思就好

T = int(input())
for _ in range(T):
    ans = 8
    a = list(map(int, input().split()))
    a = [[a[i], a[i + 1]] for i in range(0, 6, 2)]
    for i in range(3):
        for x1 in range(2):
            for j in range(3):
                if i == j:
                    continue
                for x2 in range(2):
                    if a[i][x1] == a[j][x2]:
                        ans = min(ans, 6)
                    for k in range(3):
                        if j == k or i == k:
                            continue
                        for x3 in range(2):
                            if a[i][x1] == a[j][x2] and a[j][x2] == a[k][x3]:
                                ans = min(ans, 4)
                            if a[i][x1] == a[j][x2] + a[k][x3]:
                                if a[j][x2 ^ 1] == a[k][x3 ^ 1]:
                                    ans = min(ans, 4)
                                else:
                                    ans = min(ans, 6)
    print(ans)

E题
你敢信嘛,这题我忘了自己打的是个暴力了,于是忘记回去打正解了,一直想最后一题了
不过赛后想了一下,也没想到比较好打的正解,有好想法可以分享下
暴力就直接模拟,按题目意思来

s = input()
tag = 1
while tag and len(s):
    n = len(s)
    a = [0] * n
    ns = ''
    for i in range(1, n - 1):
        if s[i] == s[i - 1] and s[i] != s[i + 1]:
            a[i], a[i + 1] = 1, 1
        if s[i] != s[i - 1] and s[i] == s[i + 1]:
            a[i], a[i - 1] = 1, 1
    for i in range(n):
        if a[i] == 0:
            ns += s[i]
    if s != ns:
        s = ns
        tag = 1
    else:
        tag = 0
if len(s) == 0:
    print('EMPTY')
else:
    print(s)

F题,差分+贪心
既然可以随便排列,肯定让加和最多次的和最大的数相乘,如此可以得最大值
题面数据范围我也不晓得是不是有问题,怕出错,所以在l,r那边加了个判断

n = int(input())
a = [0] + list(map(int, input().split()))
m = int(input())
s = [0] * (n + 10)
ans, nans = 0, 0
for _ in range(m):
    l, r = map(int, input().split())
    if l > n:
        continue
    r = min(r, n)
    s[l] += 1
    s[r + 1] -= 1
for i in range(1, n + 1):
    s[i] += s[i - 1]
    ans += s[i] * a[i]
s.sort(reverse=True)
a.sort(reverse=True)
for i in range(n):
    nans += a[i] * s[i]
print(nans - ans)

G题,打表找的规律
打表切入点:对排列中每个数的具体贡献值进行数据分析
下面是用于打表的辅助程序,你们自己运行下也能看出规律

from itertools import permutations

n = int(input())
a = [i for i in range(1, n + 1)]
tot = [0] * (n + 1)
for s in permutations(a):
    for i in range(n):
        for j in range(i):
            tot[s[i]] += s[j] < s[i]
print(tot)

赛时程序

n = int(input())
ans = 1
for i in range(3, n + 1):
    ans *= i
    ans %= 998244353
ans *= (n * (n - 1) // 2) % 998244353
ans %= 998244353
print(ans)

H题,经典最长不下降子序列DP的变种
思路是先找到一个最长不下降子序列并记录位置,然后对最长不下降子序列的每个位置进行DP
一定注意边界问题
对于最长不下降子序列的单个位置,将其后面k位置全换成该位置的值是最优策略,以此为基础进行DP

from bisect import bisect

n, k = map(int, input().split())
a = [0] + list(map(int, input().split()))
dp = [0]
pos = [0]
for i in range(1, n + 1):
    if a[i] >= dp[-1]:
        dp.append(a[i])
        pos.append(i)
        continue
    p = bisect(dp, a[i])
    dp[p] = a[i]
    pos[p] = i
ans = 0
for i in range(1, len(dp)):
    l = min(pos[i] + k, n)#往后修改k个到达的位置
    r = bisect(pos, l)#l位置在最长不下降子序列的相对位置
    if r == len(pos):#当前位置已超出子序列范围
        #i为这个节点及其前面多少节点,l-pos[i]为新增的
        ans = max(ans, i + l - pos[i])
    else:#未超过
        #未超过则还要加上子序列后面剩下的长度
        ans = max(ans, i + l - pos[i] + len(pos) - r)
print(ans)

I题,差分+贪心
我是想着从左边开始,找一个最长的连续单调序列,单调递增或者单调递减都行,然后对该序列进行处理,再继续往右移动找单调序列
代码打的挺丑的,估计是错的,建议别看

n, k = map(int, input().split())
a = list(map(int, input().split()))
st = []
ans = 0
for i in range(n):
    if i == n - 1:
        st.append(a[i])
        s = [0] * (len(st) + 1)
        if st[0] >= st[-1]:
            st = st[::-1]
        for j in range(len(st)):
            if j > 0:
                s[j] += s[j - 1]
            st[j] -= s[j]
            ans += st[j]
            s[min(j + 1, len(st))] += st[j]
            s[min(j + k, len(st))] -= st[j]
        break
    if len(st) < 2 or a[i] == st[-1]:
        st.append(a[i])
    else:
        if (a[i] < st[-1]) == (st[-1] < st[0]):
            st.append(a[i])
        else:
            s = [0] * (len(st) + 1)
            if st[0] >= st[-1]:
                st = st[::-1]
            for j in range(len(st)):
                if j > 0:
                    s[j] += s[j - 1]
                st[j] -= s[j]
                ans += st[j]
                s[min(j + 1, len(st))] += st[j]
                s[min(j + k, len(st))] -= st[j]
print(ans)

J题,没想到什么好算法
直接暴力

import math

T = int(input())
for _ in range(T):
    a = int(input())
    i = 2
    p = a
    tag = 0
    while i * i <= a:
        if a % i == 0:
            tot = 1
            while a % i == 0:
                a //= i
                tot *= i
            if tot != i:
                s = p // tot
                if int(math.sqrt(s)) ** 2 == s:
                    tag = 1
                    break
        i += 1
    if tag:
        print('yes')
    else:
        print('no')
评论 (6)