第十三届蓝桥杯省赛Python 组

第十三届蓝桥杯省赛 Python B组

写在前面

本次所有代码均存放于仓库:

Github:GxlHus/Algorithm at 蓝桥杯 (github.com)

Gitee:Algorithm: 算法解题 - Gitee.com

原题目:第十三届蓝桥杯大赛软件赛省赛_PB.pdf · AYO/Algorithm - Gitee.com

本次省赛题目总体来说不难,总体质量比较高,尤其是后边几道题,虽然能轻易做出来,但是想跑通所有测试点,还得下功夫优化,做起来比较有意思,不像之前有些题做出来就满分,做不出来就毫无头绪,参与感不强。。

把自己的解题思路写在这里,也仅代表我个人理解得出的思路,不代表官方答案,欢迎大家交流,指正错误。

试题A:排列字母

本题总分:5 分

【问题描述】

小蓝要把一个字符串中的字母按其在字母表中的顺序排列。 例如,LANQIAO 排列后为 AAILNOQ。 又如,GOODGOODSTUDYDAYDAYUP 排列后为 AADDDDDGGOOOOPSTUUYYY
请问对于以下字符串,排列之后字符串是什么? WHERETHEREISAWILLTHEREISAWAY

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个由大写字母组成的字符串,在提交答案时只填写这个字符串,填写多余的内 容将无法得分。


【思路分析】

这个没啥难的,Python直接转为listsorted即可,一行搞定

【完整代码】

s = 'WHERETHEREISAWILLTHEREISAWAY'
print(''.join(sorted(list(s))))

【参考答案】

AAAEEEEEEHHHIIILLRRRSSTTWWWY

试题B:寻找整数

本题总分:5 分

【问题描述】

有一个不超过 1 0 17 10^{17} 1017的正整数 n n n,知道这个数除以 2 2 2 49 49 49后的余数如下表 所示,求这个正整数最小是多少。

a n mod a a n mod a a n mod a a n mod a
2 1 14 11 26 23 38 37
3 2 15 14 27 20 39 23
4 1 16 9 28 25 40 9
5 4 17 0 29 16 41 1
6 5 18 11 30 29 42 11
7 4 19 18 31 27 43 11
8 1 20 9 32 25 44 33
9 2 21 11 33 11 45 29
10 9 22 11 34 17 46 15
11 0 23 15 35 4 47 5
12 5 24 17 36 29 48 41
13 10 25 9 37 22 49 46

【答案提交】

这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。


【思路分析】

这个题思路很直,直接爆。但爆也有爆的技巧,可以先算一部分,然后得到一组符合条件的,作为步长,再筛选一轮,这样步长比较大的时候,列表中的数量较少,爆起来速度不至于太慢。

至于为什么不直接写大循环,因为我每次生成的范围和结果不一样,如果筛选后剩余的列表元素过多,需要将取余的范围加大,需要实时动态调整。

① 先给一个初值:

ls = [i for i in range(1, 10 ** 4)]

② 再进行一次筛选:

for i in range(2, 10):
    ls = list(filter(lambda x: x % i == mp[i], ls))

③ 然后进行一次输出:

[1649, 4169, 6689, 9209]

④ 然后以ls[0]作为初值,以ls[1]-ls[0]作为步长,再生成一组

ls = [i for i in range(ls[0], 10 ** 9, ls[1] - ls[0])]

⑤ 再筛选,这个筛选的范围不是固定的,10~20,~15都可以,但要根据上边的lsend值决定,如果太小,则筛选范围相应小一些,反之则大一些:

for i in range(10, 20):
    ls = list(filter(lambda x: x % i == mp[i], ls))

⑥ 输出:

[88209209, 321001769, 553794329, 786586889]

重复④⑤⑥执行

...

直到即得出最终结果

ls = [i for i in range(ls[0], 10 ** 17, ls[1] - ls[0])]
for i in range(30, 50):
    ls = list(filter(lambda x: x % i == mp[i], ls))
print(ls)

【完整代码】

mp = {2: 1, 3: 2, 4: 1, 5: 4, 6: 5, 7: 4, 8: 1, 9: 2, 10: 9, 11: 0, 12: 5, 13: 10, 14: 11, 15: 14, 16: 9, 17: 0, 18: 11,19: 18, 20: 9, 21: 11, 22: 11, 23: 15, 24: 17, 25: 9, 26: 23, 27: 20, 28: 25, 29: 16, 30: 29, 31: 27, 32: 25,33: 11, 34: 17, 35: 4, 36: 29, 37: 22, 38: 37, 39: 23, 40: 9, 41: 1, 42: 11, 43: 11, 44: 33, 45: 29, 46: 15, 47: 5, 48: 41, 49: 46}

ls = [i for i in range(1, 10 ** 4)]
for i in range(2, 10):
    ls = list(filter(lambda x: x % i == mp[i], ls))
print(ls)
ls = [i for i in range(ls[0], 10 ** 9, ls[1] - ls[0])]
for i in range(10, 20):
    ls = list(filter(lambda x: x % i == mp[i], ls))
print(ls)
ls = [i for i in range(ls[0], 10 ** 13, ls[1] - ls[0])]
for i in range(20, 30):
    ls = list(filter(lambda x: x % i == mp[i], ls))
print(ls)
ls = [i for i in range(ls[0], 10 ** 17, ls[1] - ls[0])]
for i in range(30, 50):
    ls = list(filter(lambda x: x % i == mp[i], ls))
print(ls)

【参考答案】

2022040920220409

试题C: 纸张尺寸

时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分

【问题描述】

在 ISO 国际标准中定义了 A0 纸张的大小为 1189 m m × 841 m m 1189mm × 841mm 1189mm×841mm,将 A 0 A0 A0纸沿长边对折后为 A 1 A1 A1纸,大小为 841 m m × 594 m m 841mm × 594mm 841mm×594mm,在对折的过程中长度直接取下整(实际裁剪时可能有损耗)。将 A 1 A1 A1
纸沿长边对折后为 A 2 A2 A2纸,依此类推。

输入纸张的名称,请输出纸张的大小。

【输入格式】

输入一行包含一个字符串表示纸张的名称,该名称一定是 A 0 A0 A0 A 1 A1 A1 A 2 A2 A2 A 3 A3 A3 A 4 A4 A4 A 5 A5 A5 A 6 A6 A6 A 7 A7 A7 A 8 A8 A8 A 9 A9 A9之一。

【输出格式】

输出两行,每行包含一个整数,依次表示长边和短边的长度。

【样例输入 1

A0

【样例输出 1

1189
841

【样例输入 2

A1

【样例输出 2

841
594

【思路分析】

这个题没啥难度,找到长边然后除以2就行。

注意一个坑:不是长宽同时除以2,而是长边。折叠之后长边会变成短边,所以要进行交换

【完整代码】

paper = input()
width = 1189
height = 841
count = int(paper[1])
while count > 0:
    if width < height:
        width, height = height, width
    width //= 2
    count -= 1
if width < height:
    width, height = height, width
print(width)
print(height)

试题D:数位排序

时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分

【问题描述】

小蓝对一个数的数位之和很感兴趣,今天他要按照数位之和给数排序。当两个数各个数位之和不同时,将数位和较小的排在前面,当数位之和相等时,将数值小的排在前面。

例如, 2022 2022 2022排在 409 409 409前面,因为 2022 2022 2022的数位之和是 6 6 6,小于 409 409 409的数位之和 13 13 13

又如, 6 6 6排在 2022 2022 2022前面,因为它们的数位之和相同,而 6 6 6小于 2022 2022 2022

给定正整数 n n n m m m,请问对 1 1 1 n n n采用这种方法排序时,排在第 m m m个的元素是多少?

【输入格式】

输入第一行包含一个正整数 n n n。第二行包含一个正整数 m m m

【输出格式】

输出一行包含一个整数,表示答案。

【样例输入】

13
5

【样例输出】

3

【样例说明】

1 1 1 13 13 13的排序为: 1 , 10 , 2 , 11 , 3 , 12 , 4 , 13 , 5 , 6 , 7 , 8 , 9 1, 10, 2, 11, 3, 12, 4, 13, 5, 6, 7, 8, 9 1,10,2,11,3,12,4,13,5,6,7,8,9。第 5 5 5个数为 3 3 3

【评测用例规模与约定】

对于 30 % 30\% 30%的评测用例, 1 ≤ m ≤ n ≤ 300 1 ≤ m ≤ n ≤ 300 1mn300

对于 50 % 50\% 50%的评测用例, 1 ≤ m ≤ n ≤ 1000 1 ≤ m ≤ n ≤ 1000 1mn1000

对于所有评测用例, 1 ≤ m ≤ n ≤ 106 1 ≤ m ≤ n ≤ 106 1mn106


【思路分析】

这个题也不难,首先是计算数位和,直接拆成列表,然后逐个取int(),再求列表的和即可

def swh(n):
    n = map(int, list(str(n)))
    return sum(n)

然后用字典生成式生成字典然后排序后按要求取值即可。

【完整代码】

def swh(n):
    n = map(int, list(str(n)))
    return sum(n)


def lst(end, start=1):
    ls = [i for i in range(start, end + 1)]
    sw = {i: swh(i) for i in ls}
    s = sorted(sw.items(), key=lambda x: x[1])
    return s


end = int(input())
i = int(input()) - 1
print(lst(end)[i][0])

试题E:蜂巢

时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分

【问题描述】

蜂巢由大量的六边形拼接而成,定义蜂巢中的方向为: 0 0 0表示正西方向, 1 1 1表示西偏北 60 ° 60° 60° 2 2 2表示东偏北 60 ° 60° 60° 3 3 3表示正东, 4 4 4表示东偏南 60 ° 60° 60° 5 5 5表示西偏南 60 ° 60° 60°

对于给定的一点 O O O,我们以 O O O为原点定义坐标系,如果一个点 A A A O O O点先向 d d d方向走 p p p步再向 ( d + 2 ) m o d 6 (d + 2) mod 6 (d+2)mod6方向( d d d的顺时针 120 ° 120° 120°方向)走 q q q步到达,则这个点的坐标定义为$(d; p; q)
。 在 蜂 窝 中 , 一 个 点 的 坐 标 可 能 有 多 种 。 下 图 给 出 了 点 。在蜂窝中,一个点的坐标可能有多种。下图给出了点 B(0; 5; 3) 和 点 和点 C(2; 3; 2)$的示意。

image-20220409181717855

给定点 ( d 1 , p 1 , q 1 ) (d_1, p_1, q_1) (d1,p1,q1)和点$(d_2, p_2, q_2),请问他们之间最少走多少步可以到达?

【输入格式】

输入一行包含 6 个整数 d 1 , p 1 , q 1 , d 2 , p 2 , q 2 d_1, p_1, q_1, d_2, p_2, q_2 d1,p1,q1,d2,p2,q2表示两个点的坐标,相邻两个整数之间使用一个空格分隔。

【输出格式】

输出一行包含一个整数表示两点之间最少走多少步可以到达。

【样例输入】

0 5 3 2 3 2

【样例输出】

7

【评测用例规模与约定】

对于 25 25% 25的评测用例, p 1 , p 2 ≤ 103 p1, p2 ≤ 103 p1,p2103

对于 50 50% 50的评测用例, p 1 , p 2 ≤ 105 p1, p2 ≤ 105 p1,p2105

对于 75 75% 75的评测用例, p 1 , p 2 ≤ 107 p1, p2 ≤ 107 p1,p2107

对于所有评测用例, 0 ≤ d 1 , d 2 ≤ 5 , 0 ≤ q 1 < p 1 ≤ 109 , 0 ≤ q 2 < p 2 ≤ 109 0 ≤ d1, d2 ≤ 5,0 ≤ q1 < p1 ≤ 109,0 ≤ q2 < p2 ≤ 109 0d1,d250q1<p11090q2<p2109


【思路分析】

这个题,我不会,但我觉得,将路径看成三角形,已知角度,已知两边和,求出第三边的长,然后得到两点的斜边长,然后再根据夹角算两边的长度。

但是我也不知道对不对,说来惭愧,鄙人三角形学的非常辣鸡,不知道怎么求非直角三角形的第三边长,所以只有这么个想法,并没有实现。。。

如果思路不对,还请大佬赐教,如果思路对的话,麻烦大佬实现

试题F:消除游戏

时间限制: 3.0s 内存限制: 512.0MB 本题总分:15 分

【问题描述】

在一个字符串 S S S中,如果 S i = S i − 1 S_i = S_{i−1} Si=Si1 S i ∗ S i ₊ 1 S_i * S_{i₊1} SiSi1,则称 S i S_i Si S i ₊ 1 S_{i₊1} Si1为边缘字符。如果 S i ∗ S i − 1 S_i * S_{i−1} SiSi1 S i = S i ₊ 1 S_i = S_{i₊1} Si=Si1,则 S i − 1 S_ {i−1} Si1 S i S_i Si也称为边缘字符。其它的字符都不是边缘字符。

对于一个给定的串 S S S,一次操作可以一次性删除该串中的所有边缘字符(操作后可能产生新的边缘字符)。

请问经过 2 64 2^{64} 264次操作后,字符串 S S S变成了怎样的字符串,如果结果为空则输出 EMPTY

【输入格式】

输入一行包含一个字符串 S 。

【输出格式】

输出一行包含一个字符串表示答案,如果结果为空则输出 EMPTY

【样例输入 1】

edda

【样例输出 1】

EMPTY

【样例输入 2】

sdfhhhhcvhhxcxnnnnshh

【样例输出 2】

s

【评测用例规模与约定】

对于 25 25% 25的评测用例, ∣ S ∣ ≤ 1 0 3 |S | ≤ 10^3 S103,其中 ∣ S ∣ |S | S表示 S S S的长度;对于 50 50% 50的评测用例, ∣ S ∣ ≤ 1 0 3 |S | ≤ 10^3 S103

对于 75 75% 75的评测用例, ∣ S ∣ ≤ 1 0 3 |S | ≤ 10^3 S103

对于所有评测用例, ∣ S ∣ ≤ 1 0 6 |S | ≤ 10^6 S106 S S S中仅含小写字母。


【思路分析】

这个题没有好的思路,只能暴力模拟了

需要留意几个地方:

  • 不能直接删除项目中边缘字符,如果直接删除会导致越界
  • 也不能直接将待删除的边缘字符替换成其他字符,因为这样会导致判断出问题,比如xxvxv待删除,将其替换为0,变为x00,则执行下一步的时候就会将x0作为边缘字符删掉x
  • 其实循环不了 2 64 2^{64} 264次,如果化到最简,则可以比较处理前和处理后的结果,如果相同,则已经是最简了,直接跳出循环即可

两个解决办法,一个是先判断逆向字符,比如xxv,再判断正向字符:xvv

另一个是使用一个集合将待删除的下标保存,然后循环结束后统一删除。

【完整代码】

def remove_edge():
    global s
    rm = set()
    for i in range(1, len(s) - 1):
        if s[i] == s[i + 1] and s[i - 1] != s[i]:
            rm.add(i - 1)
            rm.add(i)
        if s[i] == s[i - 1] and s[i] != s[i + 1]:
            rm.add(i)
            rm.add(i + 1)
    for i in rm:
        s[i] = ''
    s = list(''.join(s))


s = list(input())
for i in range(2 ** 64):
    ls = s
    remove_edge()
    if ls == s:
        print(''.join(s))
        break
    if len(s) == 0:
        print('EMPTY')
        break
else:
    print(''.join(s))

试题G:全排列的价值

时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分

【问题描述】

对于一个排列 A = ( a 1 , a 2 , . . . , a n ) A = (a_1, a_2,..., a_n) A=(a1,a2,...,an),定义价值 c i c_i ci a 1 a_1 a1 a i − 1 a_{i-1} ai1中小于 a i a_i ai的数的个数,即 b i = ∣ { a j ∣ j < i , a j < a i } ∣ b_i = |\{a_j| j < i, a_j < a_i\}| bi={ajj<i,aj<ai}。定义
A A A的价值为 ∑ i = 1 n c i \sum^n_{i=1}c_i i=1nci

给定 n n n,求 1 1 1 n n n的全排列中所有排列的价值之和。

【输入格式】

输入一行包含一个整数 n n n

【输出格式】

输出一行包含一个整数表示答案,由于所有排列的价值之和可能很大,请输出这个数除以 998244353 998244353 998244353的余数。

【样例输入 1】

3

【样例输出 1】

9

【样例输入 2】

2022

【样例输出 2】

593300958

【样例说明】

1 至 3 构成的所有排列的价值如下:

(1, 2, 3) : 0 + 1 + 2 = 3 ;
(1, 3, 2) : 0 + 1 + 1 = 2 ;
(2, 1, 3) : 0 + 0 + 2 = 2 ;
(2, 3, 1) : 0 + 1 + 0 = 1 ;
(3, 1, 2) : 0 + 0 + 1 = 1 ;
(3, 2, 1) : 0 + 0 + 0 = 0 ;

故总和为 3 + 2 + 2 + 1 + 1 = 9 3 + 2 + 2 + 1 + 1 = 9 3+2+2+1+1=9

【评测用例规模与约定】

对于 40 % 40\% 40%的评测用例, n ≤ 20 n ≤ 20 n20

对于 70 % 70\% 70%的评测用例, n ≤ 5000 n ≤ 5000 n5000;对于所有评测用例, 2 ≤ n ≤ 1 0 6 2 ≤ n ≤ 10^6 2n106


【思路分析】

这道题比较有意思,暴力肯定不行。

首先,试想一下, 2 2 2 1 1 1的价值,只有在 [ 1 , 2 ] [1, 2] [1,2]的情况下才有价值为1.

在(1, 2, 3)的组合里边,2在1前边有三种情况,分别是:

(1, 2, 3)
(1, 3, 2)
(3, 1, 2)

这三种的情况,也就是2产生的值为3*1,然后再考虑3的位置和价值,3是当前组合里边最大的数,当3置于最后边,可以产生的价值为:2*2,当3的价值置于中间,所产生的价值为1*2

所以(1, 2, 3)全排列的值为: 3 ∗ 1 + 2 ∗ 2 + 1 ∗ 2 3*1+2*2+1*2 31+22+12。数据量太小,还看不出什么规律。我们再做一下(1, 2, 3, 4)的排列。

首先(1, 2, 3)的全排列为9,然后有4个位置可以插入4,所以(1, 2, 3, 4)的全排列中,(1, 2, 3)产生的价值为: 9 ∗ 4 = 36 9*4=36 94=36,当4置于末尾时,(1, 2, 3)
每一种排列方式都能给4带来3的价值,所以即当4置于末尾时即为 3 ∗ A 3 3 3*A_3^3 3A33,当4置于第3位,则(1, 2, 3)每一种排列,都能给4带来2的价值,即为 3 ∗ A 2 2 3*A_2^2 3A22。然后是第二位…第一位…

由此可以推得,某一位数的全排列值,等于他本身乘以前一个数的全排列值,然后再加上 ∑ i = 1 n − 1 i A n − 1 n − 1 \sum_{i=1}^{n-1}iA_{n-1}^{n-1} i=1n1iAn1n1 A n n A_n^n Ann n ! n! n!,即可得公式

c n = n ∗ ( n − 1 ) + ∑ i = 1 n − 1 i ∗ ( n − 1 ) ! c_n=n*(n-1)+\sum_{i=1}^{n-1}i*(n-1)! cn=n(n1)+i=1n1i(n1)!

∑ i = 1 n − 1 i ∗ ( n − 1 ) ! \sum_{i=1}^{n-1}i*(n-1)! i=1n1i(n1)!可以进一步化简为 ( n − 1 ) ∗ ( 1 + n − 1 ) 2 ∗ ( n − 1 ) ! \frac{(n-1)*(1+n-1)}{2}*(n-1)! 2(n1)(1+n1)(n1)!,即 ( n − 1 ) ∗ n 2 ∗ ( n − 1 ) ! \frac{(n-1)*n}{2}*(n-1)! 2(n1)n(n1)!

故可以得出递归函数

@functools.lru_cache(10 ** 6)
def get_value(n):
    m = n - 1
    jcc = jc(m) # jc(),自定义函数,求阶乘
    v = m * n // 2
    return (n * get_value(n - 1) + jcc * v) % 998244353
  • 这个递归很好理解,但是实际跑的时候数据稍大就会栈溢出,所以得给他改成循环。
  • 这里阶乘要用自定义函数加缓存,因为每一个数得价值,都会求一次阶乘,反复求的话自定义的函数加缓存在效率上相比于math.factorial()提升会非常明显,例如样例的2022,缓存求递归仅需要0.01s
    ,而用math.factorial()则需要0.1s
  • 跑了一下, 5000 5000 5000没啥问题,能过 70 % 70\% 70%的样例,但是跑不了 1 0 6 10^6 106,估计把阶乘的求法再优化优化能跑通

【完整代码】

import functools
import math


@functools.lru_cache(10 ** 6)
def jc(n):
    if n <= 1:
        return 1
    return n * jc(n - 1)


n = int(input())
v = [0, 1]
for i in range(1, n + 1):
    m = i - 1
    jcc = jc(m)
    l = (m * i) // 2
    v[1] = ((v[0] * i + l * jcc) % 998244353)
    v[0] = v[1]
print(v[1])

试题H:技能升级

时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分

【问题描述】

小蓝最近正在玩一款 RPG 游戏。他的角色一共有 N 个可以加攻击力的技能。其中第 i 个技能首次升级可以提升 A i A_i Ai点攻击力,以后每次升级增加的点数都会减少 B i B_i Bi ⌈ A i ⌉ ⌈ Ai ⌉ Ai(上取整)

次之后,再升级该技能将不会改变攻击力。

现在小蓝可以总计升级 M 次技能,他可以任意选择升级的技能和次数。请你计算小蓝最多可以提高多少点攻击力?

【输入格式】

输入第一行包含两个整数 N N N M M M。 以下 N N N行每行包含两个整数 A i A_i Ai B i B_i Bi

【输出格式】

输出一行包含一个整数表示答案。

【样例输入】

3 6
10 5
9 2
8 1

【样例输出】

47

【评测用例规模与约定】

对于 40 % 40\% 40%的评测用例, 1 ≤ N , M ≤ 1000 1 ≤ N, M ≤ 1000 1N,M1000

对于 60 % 60\% 60%的评测用例, 1 ≤ N ≤ 1 0 4 , 1 ≤ M ≤ 1 0 7 1 ≤ N ≤ 10^4, 1 ≤ M ≤ 10^7 1N104,1M107

对于所有评测用例, 1 ≤ N ≤ 1 0 5 , 1 ≤ M ≤ 2 × 1 0 5 , 1 ≤ A i , B i ≤ 1 0 5 1 ≤ N ≤ 10^5,1 ≤ M ≤ 2 × 10^5,1 ≤ Ai, Bi ≤ 10^5 1N1051M2×1051Ai,Bi105


【思路分析】

这个第一想法就是直接列表,降序,加攻击,减去攻击加成,再降序…

简单的不像20分的题,,因为前一题推了将近1个小时,怕后边时间不够,所以尽管潜意识觉得有坑,但还是没有深究

【完整代码】

n, m = map(int, input().split())
add = []
ans = 0
for i in range(n):
    a, b = map(int, input().split())
    add.append([a, b])
while m > 0:
    add = sorted(add, key=lambda x: x[0], reverse=True)
    ans += add[0][0]
    add[0][0] -= add[0][1]
    m -= 1
print(ans)

试题I: 最长不下降子序列

时间限制: 1.0s 内存限制: 512.0MB 本题总分:25 分

【问题描述】

给定一个长度为 N 的整数序列: A ₁ , A ₂ , ⋅ ⋅ ⋅ , A N A₁, A₂, · · · , AN A,A,,AN。现在你有一次机会,将其中连续的 K K K

个数修改成任意一个相同值。请你计算如何修改可以使修改后的数列的最长不下降子序列最长,请输出这个最长的长度。

最长不下降子序列是指序列中的一个子序列,子序列中的每个数不小于在它之前的数。

【输入格式】

输入第一行包含两个整数 N N N K K K。 第二行包含 N N N个整数 A ₁ , A ₂ , ⋅ ⋅ ⋅ , A N A₁, A₂, · · · , A_N A,A,,AN

【输出格式】

输出一行包含一个整数表示答案。

【样例输入】

5 1
1 4 2 8 5

【样例输出】

4

【评测用例规模与约定】

对于 20% 的评测用例, 1 ≤ K ≤ N ≤ 100 1 ≤ K ≤ N ≤ 100 1KN100;对于 30% 的评测用例, 1 ≤ K ≤ N ≤ 1000 1 ≤ K ≤ N ≤ 1000 1KN1000

对于 50% 的评测用例, 1 ≤ K ≤ N ≤ 10000 1 ≤ K ≤ N ≤ 10000 1KN10000

对于所有评测用例, 1 ≤ K ≤ N ≤ 10 ⁵ , 1 ≤ A i ≤ 10 ⁶ 1 ≤ K ≤ N ≤ 10⁵,1 ≤ Ai ≤ 10⁶ 1KN101Ai10


【思路分析】

这个题,没想到好的思路,只有硬干。。。根据题目描述一行一行写…两层没优化的循环,复杂度 O ( n 2 ) O(n^2) O(n2),不指望能跑通了

【完整代码】

import copy


# 判断非递减
def isfdj(lst):
    return lst == sorted(lst)


n, k = map(int, input().split())
ls = list(map(int, input().split()))
l = []
for i in range(1, n - k):
    tmp = copy.deepcopy(ls)
    t = ls[i - 1]
    for j in range(i, i + k):
        tmp[j] = t
    ll = 0 # 当前子串长度
    low = 0
    high = 1
    while high < n:
        if isfdj(tmp[low:high]):
            ll += 1
        else:
            l.append(ll)
            ll = 0
            low = high
        high += 1
    l.append(ll)
print(max(l))

试题J: 最优清零方案

时间限制: 5.0s 内存限制: 512.0MB 本题总分:25 分

【问题描述】

给定一个长度为 N N N的数列 A ₁ , A ₂ , ⋅ ⋅ ⋅ , A N A₁, A₂, · · · , A_N A,A,,AN。现在小蓝想通过若干次操作将这个数列中每个数字清零。

每次操作小蓝可以选择以下两种之一:

  1. 选择一个大于 0 0 0的整数,将它减去 1 1 1
  2. 选择连续 K K K个大于 0 0 0的整数,将它们各减去 1 1 1。小蓝最少经过几次操作可以将整个数列清零?

【输入格式】

输入第一行包含两个整数 N N N K K K

第二行包含 N N N个整数 A ₁ , A ₂ , ⋅ ⋅ ⋅ , A N A₁, A₂, · · · , A_N A,A,,AN

【输出格式】

输出一个整数表示答案。

【样例输入】

4 2
1 2 3 4

【样例输出】

6

【评测用例规模与约定】

对于 20 % 20\% 20%的评测用例, 1 ≤ K ≤ N ≤ 10 1 ≤ K ≤ N ≤ 10 1KN10

对于 40 % 40\% 40%的评测用例, 1 ≤ K ≤ N ≤ 100 1 ≤ K ≤ N ≤ 100 1KN100

对于 50 % 50\% 50%的评测用例, 1 ≤ K ≤ N ≤ 1000 1 ≤ K ≤ N ≤ 1000 1KN1000

对于 60 % 60\% 60%的评测用例, 1 ≤ K ≤ N ≤ 10000 1 ≤ K ≤ N ≤ 10000 1KN10000

对于 70 % 70\% 70%的评测用例, 1 ≤ K ≤ N ≤ 100000 1 ≤ K ≤ N ≤ 100000 1KN100000

对于所有评测用例, 1 ≤ K ≤ N ≤ 1000000 , 0 ≤ A i ≤ 1000000 1 ≤ K ≤ N ≤ 1000000, 0 ≤ A_i ≤ 1000000 1KN1000000,0Ai1000000


【思路分析】

这个题就没啥好分析的了,甚至暴力模拟都没想好怎么模拟,直接用了贪心,从前往后挨个减一遍。猜着应该要用动规,但没想出来怎么个规法。分享下自己的残缺代码,也欢迎大佬赐教。

【完整代码】

n, k = map(int, input().split())
lst = list(map(int, input().split()))
red = lambda x: x - 1
i = 0
c = 0
while True:
    r = i + k
    if r <= n:
        if 0 not in lst[i:r]:
            lst[i: r] = map(red, lst[i:r])
            c += 1
        else:
            i = lst[i: r].index(0) + i + 1
    else:
        break
c += sum(lst)
print(c)

你可能感兴趣的:(算法_蓝桥杯,算法,第十三届蓝桥杯,python)